Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for heart languages #195

Merged
merged 9 commits into from
May 16, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@ package org.bibletranslationtools.fetcher.di

import org.bibletranslationtools.fetcher.config.DevEnvironmentConfig
import org.bibletranslationtools.fetcher.config.EnvironmentConfig
import org.bibletranslationtools.fetcher.impl.repository.AvailabilityCacheAccessor
import org.bibletranslationtools.fetcher.impl.repository.BookCatalogImpl
import org.bibletranslationtools.fetcher.impl.repository.BookRepositoryImpl
import org.bibletranslationtools.fetcher.impl.repository.ChapterCatalogImpl
import org.bibletranslationtools.fetcher.impl.repository.ContentAvailabilityCacheBuilder
import org.bibletranslationtools.fetcher.impl.repository.DirectoryProviderImpl
import org.bibletranslationtools.fetcher.impl.repository.LangType
import org.bibletranslationtools.fetcher.impl.repository.LanguageRepositoryImpl
import org.bibletranslationtools.fetcher.impl.repository.PortGatewayLanguageCatalog
import org.bibletranslationtools.fetcher.impl.repository.ProductCatalogImpl
import org.bibletranslationtools.fetcher.impl.repository.RCRepositoryImpl
import org.bibletranslationtools.fetcher.impl.repository.StorageAccessImpl
import org.bibletranslationtools.fetcher.impl.repository.UnfoldingWordHeartLanguagesCatalog
import org.bibletranslationtools.fetcher.impl.repository.UnfoldingWordLanguagesCatalog
import org.bibletranslationtools.fetcher.io.LocalFileTransferClient
import org.bibletranslationtools.fetcher.repository.BookCatalog
import org.bibletranslationtools.fetcher.repository.BookRepository
import org.bibletranslationtools.fetcher.repository.ChapterCatalog
import org.bibletranslationtools.fetcher.repository.ContentCacheAccessor
import org.bibletranslationtools.fetcher.repository.DirectoryProvider
import org.bibletranslationtools.fetcher.repository.LanguageCatalog
import org.bibletranslationtools.fetcher.repository.LanguageRepository
Expand All @@ -42,12 +39,13 @@ val appDependencyModule = module(createdAtStart = true) {
single<StorageAccess> { StorageAccessImpl(get()) }

single<ChapterCatalog> { ChapterCatalogImpl() }
single<LanguageCatalog>(named("GL")) { PortGatewayLanguageCatalog() }
single<LanguageCatalog>(named("HL")) { UnfoldingWordHeartLanguagesCatalog(get()) }
single<LanguageCatalog>(named(LangType.GL.name)) { UnfoldingWordLanguagesCatalog(get(), LangType.GL) }
single<LanguageCatalog>(named(LangType.HL.name)) { UnfoldingWordLanguagesCatalog(get(), LangType.HL) }
single<LanguageCatalog>(named(LangType.ALL.name)) { UnfoldingWordLanguagesCatalog(get(), LangType.ALL) }
single<LanguageRepository> {
LanguageRepositoryImpl(
get(named("GL")),
get(named("HL"))
get(named(LangType.GL.name)),
get(named(LangType.HL.name))
)
}
single<ProductCatalog> { ProductCatalogImpl() }
Expand All @@ -56,16 +54,4 @@ val appDependencyModule = module(createdAtStart = true) {
single<ResourceContainerRepository> { RCRepositoryImpl(get()) }

single<IDownloadClient> { LocalFileTransferClient(get()) }

single {
ContentAvailabilityCacheBuilder(
envConfig = get(),
languageCatalog = get(named("GL")),
productCatalog = get(),
chapterCatalog = get(),
bookRepository = get(),
storageAccess = get()
)
}
single<ContentCacheAccessor> { AvailabilityCacheAccessor(get()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package org.bibletranslationtools.fetcher.impl.repository
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import io.ktor.client.request.get
import io.ktor.util.error
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,32 @@ import org.bibletranslationtools.fetcher.data.Division
import org.bibletranslationtools.fetcher.repository.DirectoryProvider
import org.bibletranslationtools.fetcher.repository.FileAccessRequest
import org.bibletranslationtools.fetcher.repository.StorageAccess
import org.bibletranslationtools.fetcher.usecase.ProductFileExtension
import org.bibletranslationtools.fetcher.usecase.resourceIdByLanguage
import org.slf4j.LoggerFactory

class StorageAccessImpl(private val directoryProvider: DirectoryProvider) : StorageAccess {

companion object {
fun getPathPrefixDir(
languageCode: String,
resourceId: String,
fileExtension: String,
directoryProvider: DirectoryProvider,
bookSlug: String = "",
chapter: String = ""
): File {
return getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode,
resourceId,
fileExtension,
bookSlug,
chapter
)
}

fun getPathPrefixDir(
root: File,
languageCode: String,
resourceId: String,
fileExtension: String,
bookSlug: String = "",
chapter: String = ""
fileExtension: String? = null,
bookSlug: String? = null,
chapter: String? = null
): File {
val trimmedChapter = chapter.trimStart('0')

return when {
bookSlug.isNotEmpty() && trimmedChapter.isNotEmpty() ->
root.resolve(
"$languageCode/$resourceId/$bookSlug/$trimmedChapter/CONTENTS/$fileExtension"
)
bookSlug.isNotEmpty() -> root.resolve(
"$languageCode/$resourceId/$bookSlug/CONTENTS/$fileExtension"
)
else -> root.resolve(
"$languageCode/$resourceId/CONTENTS/$fileExtension"
)
}
val trimmedChapter = chapter?.trimStart('0')

val languagePart = languageCode
val resourcePart = "/$resourceId"
val bookPart = if (!bookSlug.isNullOrEmpty()) "/$bookSlug" else ""
val chapterPart = if (!trimmedChapter.isNullOrEmpty()) "/$trimmedChapter" else ""
val extensionPart = if (!fileExtension.isNullOrEmpty()) "/CONTENTS/$fileExtension" else ""

return root.resolve(
"$languagePart$resourcePart$bookPart$chapterPart$extensionPart"
)
}

fun getContentDir(
Expand Down Expand Up @@ -85,20 +66,43 @@ class StorageAccessImpl(private val directoryProvider: DirectoryProvider) : Stor
return directoryProvider.getContentRoot()
}

override fun getLanguageCodes(): List<String> {
override fun hasLanguageContent(languageCode: String): Boolean {
val sourceFileRootDir = directoryProvider.getContentRoot()
val dirs = sourceFileRootDir.listFiles(File::isDirectory)

return if (dirs.isNullOrEmpty()) listOf() else dirs.map { it.name }
return dirs?.any { it.name == languageCode } ?: false
}

override fun hasProductContent(languageCode: String, fileExtensions: List<String>): Boolean {
val resourceId = resourceIdByLanguage(languageCode)
val booksDir = getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode,
resourceId
)

return booksDir.listFiles(File::isDirectory)?.any { bookDir ->
val bookSlug = bookDir.name
fileExtensions.any { ext ->
val dir = getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode,
resourceId,
ext,
bookSlug
)
dir.exists()
}
} ?: false
}

override fun getBookFile(request: FileAccessRequest): File? {
val bookPrefixDir = getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode = request.languageCode,
resourceId = request.resourceId,
bookSlug = request.bookSlug,
fileExtension = request.fileExtension,
directoryProvider = directoryProvider
fileExtension = request.fileExtension
)

val grouping = getGrouping(request.fileExtension, Division.BOOK)
Expand Down Expand Up @@ -126,12 +130,12 @@ class StorageAccessImpl(private val directoryProvider: DirectoryProvider) : Stor

override fun getChapterFile(request: FileAccessRequest): File? {
val chapterPrefixDir = getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode = request.languageCode,
resourceId = request.resourceId,
bookSlug = request.bookSlug,
fileExtension = request.fileExtension,
chapter = request.chapter,
directoryProvider = directoryProvider
chapter = request.chapter
)

val grouping = getGrouping(request.fileExtension, Division.CHAPTER)
Expand Down Expand Up @@ -188,11 +192,11 @@ class StorageAccessImpl(private val directoryProvider: DirectoryProvider) : Stor
): Boolean {
for (ext in fileExtensionList) {
val bookPrefixDir = getPathPrefixDir(
directoryProvider.getContentRoot(),
languageCode = languageCode,
resourceId = resourceId,
bookSlug = bookSlug,
fileExtension = ext,
directoryProvider = directoryProvider
fileExtension = ext
)
val walkBookDir = bookPrefixDir.walk()
val grouping = getGrouping(ext, Division.BOOK)
Expand Down Expand Up @@ -238,7 +242,7 @@ class StorageAccessImpl(private val directoryProvider: DirectoryProvider) : Stor

private fun getGrouping(ext: String, division: Division): String {
return when {
ext == "tr" -> "verse"
ext == ProductFileExtension.BTTR.fileType -> "verse"
else -> division.name.toLowerCase()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import org.bibletranslationtools.fetcher.data.Language
import org.bibletranslationtools.fetcher.di.ext.CommonKoinExt.get
import org.bibletranslationtools.fetcher.repository.LanguageCatalog
import org.slf4j.LoggerFactory

private const val LANGUAGE_CODE_ID = "lc"
private const val ANGLICIZED_NAME_ID = "ang"
private const val LOCALIZED_NAME_ID = "ln"
private const val IS_GATEWAY = "gw"
private const val DIRECTION = "ld"

class UnfoldingWordHeartLanguagesCatalog(envConfig: EnvironmentConfig) : LanguageCatalog {
enum class LangType {
GL,
HL,
ALL
}

class UnfoldingWordLanguagesCatalog(
envConfig: EnvironmentConfig,
private val langType: LangType
) : LanguageCatalog {

@JsonIgnoreProperties(ignoreUnknown = true)
private data class UnfoldingWordHeartLanguage(
Expand All @@ -45,7 +54,11 @@ class UnfoldingWordHeartLanguagesCatalog(envConfig: EnvironmentConfig) : Languag

return languages
.filter {
!it.isGateway
when (langType) {
LangType.GL -> it.isGateway
LangType.HL -> !it.isGateway
LangType.ALL -> true
}
}
.map {
Language(it.code, it.anglicizedName, it.localizedName, isGateway = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.bibletranslationtools.fetcher.repository
data class FileAccessRequest(
val languageCode: String,
val resourceId: String,
val fileExtension: String,
val fileExtension: String = "",
val bookSlug: String = "",
val chapter: String = "",
val mediaExtension: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import java.io.File

interface StorageAccess {
fun getContentRoot(): File
fun getLanguageCodes(): List<String>
fun hasLanguageContent(languageCode: String): Boolean
fun hasProductContent(languageCode: String, fileExtensions: List<String>): Boolean
fun getBookFile(request: FileAccessRequest): File?
fun getChapterFile(request: FileAccessRequest): File?
fun hasBookContent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import org.bibletranslationtools.fetcher.data.ContainerExtensions
import org.bibletranslationtools.fetcher.data.Language
import org.bibletranslationtools.fetcher.data.Product
import org.bibletranslationtools.fetcher.repository.BookRepository
import org.bibletranslationtools.fetcher.repository.ContentCacheAccessor
import org.bibletranslationtools.fetcher.repository.FileAccessRequest
import org.bibletranslationtools.fetcher.repository.StorageAccess
import org.bibletranslationtools.fetcher.usecase.viewdata.BookViewData
Expand All @@ -26,34 +25,26 @@ class FetchBookViewData(

private val fileExtensionList =
if (ContainerExtensions.isSupported(productExtension.fileType)) {
listOf("tr")
listOf(ProductFileExtension.BTTR.fileType)
} else {
listOf("wav", "mp3")
listOf(ProductFileExtension.MP3.fileType, ProductFileExtension.WAV.fileType)
}

private val priorityList = listOf(
PriorityItem("mp3", "hi"),
PriorityItem("mp3", "low"),
PriorityItem("wav", "")
PriorityItem(ProductFileExtension.MP3.fileType, ProductFileQuality.HI.quality),
PriorityItem(ProductFileExtension.MP3.fileType, ProductFileQuality.LOW.quality),
PriorityItem(ProductFileExtension.WAV.fileType, "")
)

fun getViewDataList(
currentPath: String,
cacheAccessor: ContentCacheAccessor,
isGateway: Boolean = true
): List<BookViewData> {
fun getViewDataList(currentPath: String): List<BookViewData> {
val books = bookRepo.getBooks(resourceId = resourceId, languageCode = language.code)
return books.map { book ->
book.availability = if (isGateway) {
cacheAccessor.isBookAvailable(book.slug, language.code, product.slug)
} else {
storage.hasBookContent(
language.code,
resourceId,
book.slug,
fileExtensionList
)
}
book.availability = storage.hasBookContent(
language.code,
resourceId,
book.slug,
fileExtensionList
)

BookViewData(
index = book.index,
Expand All @@ -65,17 +56,9 @@ class FetchBookViewData(
}
}

fun getViewData(
bookSlug: String,
cacheAccessor: ContentCacheAccessor,
isGateway: Boolean = true
): BookViewData? {
fun getViewData(bookSlug: String): BookViewData? {
val book = bookRepo.getBook(bookSlug)
val url = if (isGateway) {
cacheAccessor.getBookUrl(bookSlug, language.code, product.slug)
} else {
getBookDownloadUrl(bookSlug)
}
val url = getBookDownloadUrl(bookSlug)

return if (book != null) BookViewData(
index = book.index,
Expand All @@ -92,9 +75,10 @@ class FetchBookViewData(
var url: String? = null
for (priority in priorityList) {
val fileAccessRequest = when (productExtension) {
ProductFileExtension.ORATURE -> return "#"
ProductFileExtension.ORATURE -> return "javascript:void(0)"
ProductFileExtension.BTTR -> getBTTRFileAccessRequest(bookSlug, priority)
ProductFileExtension.MP3 -> getMp3FileAccessRequest(bookSlug, priority)
else -> return ""
}

val bookFile = storage.getBookFile(fileAccessRequest)
Expand All @@ -113,7 +97,7 @@ class FetchBookViewData(
return FileAccessRequest(
languageCode = language.code,
resourceId = resourceId,
fileExtension = "tr",
fileExtension = ProductFileExtension.BTTR.fileType,
bookSlug = bookSlug,
mediaExtension = priorityItem.fileExtension,
mediaQuality = priorityItem.mediaQuality
Expand Down
Loading
Loading