mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Refactored the code to fetch the online library from OPDS same like Kiwix server, it first fetches the 50 items from the OPDS catelog, and load more when needed.
This commit is contained in:
parent
f3f3f224c0
commit
d9a10b30a6
@ -18,11 +18,18 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.zimManager
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||
import org.kiwix.kiwixmobile.di.modules.ONLINE_BOOKS_LIBRARY
|
||||
import org.kiwix.kiwixmobile.di.modules.ONLINE_BOOKS_MANAGER
|
||||
import org.kiwix.libkiwix.Library
|
||||
import org.kiwix.libkiwix.Manager
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserFactory
|
||||
import java.io.StringReader
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
@ -30,23 +37,26 @@ class OnlineLibraryManager @Inject constructor(
|
||||
@Named(ONLINE_BOOKS_LIBRARY) private val library: Library,
|
||||
@Named(ONLINE_BOOKS_MANAGER) private val manager: Manager,
|
||||
) {
|
||||
suspend fun parseOPDSStream(content: String?, urlHost: String): Boolean =
|
||||
runCatching {
|
||||
manager.readOpds(content, urlHost)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.isSuccess
|
||||
|
||||
suspend fun getOnlineBooks(): List<LibkiwixBook> {
|
||||
val onlineBooksList = arrayListOf<LibkiwixBook>()
|
||||
var totalResult = 0
|
||||
suspend fun parseOPDSStreamAndGetBooks(
|
||||
content: String?,
|
||||
urlHost: String
|
||||
): ArrayList<LibkiwixBook>? =
|
||||
runCatching {
|
||||
library.booksIds.forEach { bookId ->
|
||||
val book = library.getBookById(bookId)
|
||||
content?.let { totalResult = extractTotalResults(it) }
|
||||
val onlineBooksList = arrayListOf<LibkiwixBook>()
|
||||
val tempLibrary = Library()
|
||||
val tempManager = Manager(tempLibrary)
|
||||
tempManager.readOpds(content, urlHost)
|
||||
tempLibrary.booksIds.forEach { bookId ->
|
||||
val book = tempLibrary.getBookById(bookId)
|
||||
onlineBooksList.add(LibkiwixBook(book))
|
||||
}
|
||||
}.onFailure { it.printStackTrace() }
|
||||
return onlineBooksList
|
||||
}
|
||||
onlineBooksList
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.getOrNull()
|
||||
|
||||
suspend fun getOnlineBooksLanguage(): List<String> {
|
||||
return runCatching {
|
||||
@ -55,4 +65,75 @@ class OnlineLibraryManager @Inject constructor(
|
||||
it.printStackTrace()
|
||||
}.getOrDefault(emptyList())
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the URL for fetching the OPDS library entries with pagination and optional filters.
|
||||
*
|
||||
* @param baseUrl The base URL of the Kiwix library server (e.g., "https://opds.library.kiwix.org/").
|
||||
* @param start The index from which to start fetching entries (default is 0).
|
||||
* @param count The number of entries to fetch per page (default is 50).
|
||||
* @param query Optional search query for filtering results by text.
|
||||
* @param lang Optional language code filter (e.g., "en", "ita").
|
||||
* @param category Optional category filter (e.g., "wikipedia", "books").
|
||||
* @return A full URL string with query parameters applied.
|
||||
*
|
||||
* Example:
|
||||
* buildLibraryUrl("https://library.kiwix.org", start = 100, count = 50, lang = "en")
|
||||
* returns: "https://library.kiwix.org/v2/entries?start=100&count=50&lang=en"
|
||||
*/
|
||||
fun buildLibraryUrl(
|
||||
baseUrl: String,
|
||||
start: Int = 0,
|
||||
count: Int = 50,
|
||||
query: String? = null,
|
||||
lang: String? = null,
|
||||
category: String? = null
|
||||
): String {
|
||||
val params = mutableListOf("start=$start", "count=$count")
|
||||
query?.takeIf { it.isNotBlank() }?.let { params += "q=$it" }
|
||||
lang?.takeIf { it.isNotBlank() }?.let { params += "lang=$it" }
|
||||
category?.takeIf { it.isNotBlank() }?.let { params += "category=$it" }
|
||||
|
||||
return "$baseUrl/$OPDS_LIBRARY_ENDPOINT?${params.joinToString("&")}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of pages needed for pagination.
|
||||
*
|
||||
* @param totalResults Total number of items available (e.g., 3408).
|
||||
* @param pageSize Number of items per page (e.g., 50).
|
||||
* @return The total number of pages required to show all items.
|
||||
*
|
||||
* Example:
|
||||
* calculateTotalPages(3408, 50) returns 69
|
||||
*/
|
||||
fun calculateTotalPages(totalResults: Int, pageSize: Int): Int =
|
||||
(totalResults + pageSize - 1) / pageSize
|
||||
|
||||
/**
|
||||
* Calculates the starting index (offset) for a given page number.
|
||||
*
|
||||
* @param pageIndex The page number starting from 0 (e.g., pageIndex = 2 means page 3).
|
||||
* @param pageSize Number of items per page (e.g., 50).
|
||||
* @return The offset index to be used in a paginated request (e.g., start=100).
|
||||
*
|
||||
* Example:
|
||||
* getStartOffset(2, 50) returns 100
|
||||
*/
|
||||
fun getStartOffset(pageIndex: Int, pageSize: Int): Int = pageIndex * pageSize
|
||||
|
||||
private suspend fun extractTotalResults(xml: String): Int = withContext(Dispatchers.IO) {
|
||||
val factory = XmlPullParserFactory.newInstance()
|
||||
val parser = factory.newPullParser()
|
||||
parser.setInput(StringReader(xml))
|
||||
|
||||
var eventType = parser.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlPullParser.START_TAG && parser.name == "totalResults") {
|
||||
return@withContext parser.nextText().toIntOrNull() ?: ZERO
|
||||
}
|
||||
eventType = parser.next()
|
||||
}
|
||||
return@withContext ZERO
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,8 @@ import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
|
||||
import org.kiwix.kiwixmobile.core.data.DataSource
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_NETWORK_PATH
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
||||
import org.kiwix.kiwixmobile.core.data.remote.ProgressResponseBody
|
||||
import org.kiwix.kiwixmobile.core.data.remote.UserAgentInterceptor
|
||||
import org.kiwix.kiwixmobile.core.di.modules.CALL_TIMEOUT
|
||||
@ -254,7 +255,7 @@ class ZimManageViewModel @Inject constructor(
|
||||
private fun getContentLengthOfLibraryXmlFile(): Long {
|
||||
val headRequest =
|
||||
Request.Builder()
|
||||
.url("$KIWIX_OPDS_LIBRARY_URL$OPDS_LIBRARY_NETWORK_PATH")
|
||||
.url("$KIWIX_OPDS_LIBRARY_URL$OPDS_LIBRARY_ENDPOINT?count=$ITEMS_PER_PAGE")
|
||||
.head()
|
||||
.header("Accept-Encoding", "identity")
|
||||
.build()
|
||||
@ -447,18 +448,22 @@ class ZimManageViewModel @Inject constructor(
|
||||
kiwixService: KiwixService
|
||||
): Flow<List<LibkiwixBook>> = flow {
|
||||
downloadProgress.postValue(context.getString(R.string.starting_downloading_remote_library))
|
||||
val response = kiwixService.getLibrary()
|
||||
// TODO get the filter from online library and pass it here to get the online content based on filters.
|
||||
val buildUrl = onlineLibraryManager.buildLibraryUrl(
|
||||
KIWIX_OPDS_LIBRARY_URL,
|
||||
)
|
||||
val response = kiwixService.getLibraryPage(buildUrl)
|
||||
val resolvedUrl = response.raw().networkResponse?.request?.url
|
||||
?: response.raw().request.url
|
||||
val baseHostUrl = "${resolvedUrl.scheme}://${resolvedUrl.host}"
|
||||
downloadProgress.postValue(context.getString(R.string.parsing_remote_library))
|
||||
val libraryXml = response.body()
|
||||
val isLibraryParsed = onlineLibraryManager.parseOPDSStream(libraryXml, baseHostUrl)
|
||||
val onlineBooks = onlineLibraryManager.parseOPDSStreamAndGetBooks(libraryXml, baseHostUrl)
|
||||
emit(
|
||||
if (isLibraryParsed) {
|
||||
onlineLibraryManager.getOnlineBooks()
|
||||
} else {
|
||||
if (onlineBooks.isNullOrEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
onlineBooks
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -29,8 +29,10 @@ import retrofit2.http.GET
|
||||
import retrofit2.http.Url
|
||||
|
||||
interface KiwixService {
|
||||
@GET(OPDS_LIBRARY_NETWORK_PATH)
|
||||
suspend fun getLibrary(): Response<String>
|
||||
@GET
|
||||
suspend fun getLibraryPage(
|
||||
@Url url: String
|
||||
): Response<String>
|
||||
|
||||
@GET
|
||||
suspend fun getMetaLinks(
|
||||
@ -52,8 +54,7 @@ interface KiwixService {
|
||||
}
|
||||
|
||||
companion object {
|
||||
// To fetch the full OPDS catalog.
|
||||
// TODO we will change this to pagination later once we migrate to OPDS properly.
|
||||
const val OPDS_LIBRARY_NETWORK_PATH = "v2/entries?count=-1"
|
||||
const val OPDS_LIBRARY_ENDPOINT = "v2/entries"
|
||||
const val ITEMS_PER_PAGE = 50
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user