Fixing covers

This commit is contained in:
Mio.
2025-07-15 08:35:57 +02:00
parent 00d1762ff0
commit d9a19c4a50

View File

@@ -8,6 +8,7 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.AppInfo
import eu.kanade.tachiyomi.extension.all.kavita.dto.AuthenticationDto import eu.kanade.tachiyomi.extension.all.kavita.dto.AuthenticationDto
@@ -80,6 +81,7 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException import java.io.IOException
import java.security.MessageDigest import java.security.MessageDigest
import java.util.Calendar
class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSource, HttpSource() { class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSource, HttpSource() {
private val helper = KavitaHelper() private val helper = KavitaHelper()
@@ -270,7 +272,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
try { try {
val result = response.parseAs<List<SeriesDto>>() val result = response.parseAs<List<SeriesDto>>()
series = result series = result
val mangaList = result.map { item -> helper.createSeriesDto(item, apiUrl, baseUrl) } val mangaList = result.map { item -> helper.createSeriesDto(item, apiUrl, apiKey) }
return MangasPage(mangaList, helper.hasNextPage(response)) return MangasPage(mangaList, helper.hasNextPage(response))
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "Unhandled exception", e) Log.e(LOG_TAG, "Unhandled exception", e)
@@ -382,6 +384,40 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
statements = mutableListOf(), statements = mutableListOf(),
) )
// Handle text query first
if (query.isNotBlank()) {
val queryLower = query.trim().lowercase()
// First try matching people
val matchedPeople = peopleListMeta.filter {
it.name.equals(query.trim(), ignoreCase = true) ||
it.name.trim().lowercase().contains(queryLower)
}
if (matchedPeople.isNotEmpty()) {
Log.d(LOG_TAG, "Matched people for query '$query': ${matchedPeople.map { it.name }}")
matchedPeople.forEach { person ->
when (PersonRole.fromId(person.role ?: -1)) {
PersonRole.Writer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Writers, person.name)
PersonRole.Penciller -> filterV2.addStatement(FilterComparison.Matches, FilterField.Penciller, person.name)
PersonRole.Inker -> filterV2.addStatement(FilterComparison.Matches, FilterField.Inker, person.name)
PersonRole.Colorist -> filterV2.addStatement(FilterComparison.Matches, FilterField.Colorist, person.name)
PersonRole.Letterer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Letterer, person.name)
PersonRole.CoverArtist -> filterV2.addStatement(FilterComparison.Matches, FilterField.CoverArtist, person.name)
PersonRole.Editor -> filterV2.addStatement(FilterComparison.Matches, FilterField.Editor, person.name)
PersonRole.Publisher -> filterV2.addStatement(FilterComparison.Matches, FilterField.Publisher, person.name)
PersonRole.Character -> filterV2.addStatement(FilterComparison.Matches, FilterField.Characters, person.name)
PersonRole.Translator -> filterV2.addStatement(FilterComparison.Matches, FilterField.Translators, person.name)
else -> Unit
}
}
} else {
// If no people matched, search in series name and localized title
filterV2.addStatement(FilterComparison.Matches, FilterField.SeriesName, query)
Log.d(LOG_TAG, "No people matched, searching series name and localized title for: '$query'")
}
}
filters.forEach { filter -> filters.forEach { filter ->
when (filter) { when (filter) {
is Filter.Sort -> { is Filter.Sort -> {
@@ -704,37 +740,37 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
} }
// @todo Apply search query and people filters via metadata filter // @todo Apply search query and people filters via metadata filter
if (query.isNotBlank()) { // if (query.isNotBlank()) {
val queryLower = query.trim().lowercase() // val queryLower = query.trim().lowercase()
val matchedPeople = peopleListMeta.filter { // val matchedPeople = peopleListMeta.filter {
it.name.equals(query.trim(), ignoreCase = true) || // it.name.equals(query.trim(), ignoreCase = true) ||
it.name.trim().lowercase().contains(queryLower) // it.name.trim().lowercase().contains(queryLower)
} // }
//
if (matchedPeople.isNotEmpty()) { // if (matchedPeople.isNotEmpty()) {
Log.d(LOG_TAG, "Matched people for query '$query': ${matchedPeople.map { it.name }}") // Log.d(LOG_TAG, "Matched people for query '$query': ${matchedPeople.map { it.name }}")
matchedPeople.forEach { person -> // matchedPeople.forEach { person ->
when (PersonRole.fromId(person.role ?: -1)) { // when (PersonRole.fromId(person.role ?: -1)) {
PersonRole.Writer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Writers, person.name) // PersonRole.Writer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Writers, person.name)
PersonRole.Penciller -> filterV2.addStatement(FilterComparison.Matches, FilterField.Penciller, person.name) // PersonRole.Penciller -> filterV2.addStatement(FilterComparison.Matches, FilterField.Penciller, person.name)
PersonRole.Inker -> filterV2.addStatement(FilterComparison.Matches, FilterField.Inker, person.name) // PersonRole.Inker -> filterV2.addStatement(FilterComparison.Matches, FilterField.Inker, person.name)
PersonRole.Colorist -> filterV2.addStatement(FilterComparison.Matches, FilterField.Colorist, person.name) // PersonRole.Colorist -> filterV2.addStatement(FilterComparison.Matches, FilterField.Colorist, person.name)
PersonRole.Letterer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Letterer, person.name) // PersonRole.Letterer -> filterV2.addStatement(FilterComparison.Matches, FilterField.Letterer, person.name)
PersonRole.CoverArtist -> filterV2.addStatement(FilterComparison.Matches, FilterField.CoverArtist, person.name) // PersonRole.CoverArtist -> filterV2.addStatement(FilterComparison.Matches, FilterField.CoverArtist, person.name)
PersonRole.Editor -> filterV2.addStatement(FilterComparison.Matches, FilterField.Editor, person.name) // PersonRole.Editor -> filterV2.addStatement(FilterComparison.Matches, FilterField.Editor, person.name)
PersonRole.Publisher -> filterV2.addStatement(FilterComparison.Matches, FilterField.Publisher, person.name) // PersonRole.Publisher -> filterV2.addStatement(FilterComparison.Matches, FilterField.Publisher, person.name)
PersonRole.Character -> filterV2.addStatement(FilterComparison.Matches, FilterField.Characters, person.name) // PersonRole.Character -> filterV2.addStatement(FilterComparison.Matches, FilterField.Characters, person.name)
PersonRole.Translator -> filterV2.addStatement(FilterComparison.Matches, FilterField.Translators, person.name) // PersonRole.Translator -> filterV2.addStatement(FilterComparison.Matches, FilterField.Translators, person.name)
else -> { // else -> {
Log.d(LOG_TAG, "Unrecognized role for person: ${person.name}") // Log.d(LOG_TAG, "Unrecognized role for person: ${person.name}")
} // }
} // }
} // }
} else { // } else {
Log.d(LOG_TAG, "No people matched query '$query'. Falling back to SeriesName search.") // Log.d(LOG_TAG, "No people matched query '$query'. Falling back to SeriesName search.")
filterV2.addStatement(FilterComparison.Matches, FilterField.SeriesName, query) // filterV2.addStatement(FilterComparison.Matches, FilterField.SeriesName, query)
} // }
} // }
// Always apply selected people filters (even if query is blank) // Always apply selected people filters (even if query is blank)
val roleToField = mapOf( val roleToField = mapOf(
@@ -786,7 +822,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
return if (manga.url.contains("source=readinglist")) { return if (manga.url.contains("source=readinglist")) {
val readingListId = manga.url.substringAfter("readingListId=").substringBefore("&").toIntOrNull() val readingListId = manga.url.substringAfter("readingListId=").substringBefore("&").toIntOrNull()
val now = java.util.Calendar.getInstance() val now = Calendar.getInstance()
val readingList = cachedReadingLists.find { it.id == readingListId } val readingList = cachedReadingLists.find { it.id == readingListId }
if (readingList != null) { if (readingList != null) {
val groupTags = preferences.groupTags val groupTags = preferences.groupTags
@@ -835,7 +871,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
val hasDates = readingList.startingYear > 0 && readingList.endingYear > 0 val hasDates = readingList.startingYear > 0 && readingList.endingYear > 0
val status = if (hasDates) { val status = if (hasDates) {
val endCal = java.util.Calendar.getInstance().apply { val endCal = Calendar.getInstance().apply {
set(readingList.endingYear, readingList.endingMonth - 1, 1) set(readingList.endingYear, readingList.endingMonth - 1, 1)
} }
if (now.after(endCal)) SManga.PUBLISHING_FINISHED else SManga.ONGOING if (now.after(endCal)) SManga.PUBLISHING_FINISHED else SManga.ONGOING
@@ -930,9 +966,9 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.filterNot { it.equals(foundDemographic, ignoreCase = true) } .filterNot { it.equals(foundDemographic, ignoreCase = true) }
.filterNot { tag -> filteredGenres.any { genre -> genre.equals(tag, ignoreCase = true) } } .filterNot { tag -> filteredGenres.any { genre -> genre.equals(tag, ignoreCase = true) } }
val manga = helper.createSeriesDto(existingSeries, apiUrl, baseUrl) val manga = helper.createSeriesDto(existingSeries, apiUrl, apiKey)
manga.title = existingSeries.name manga.title = existingSeries.name
manga.url = "$baseUrl/Series/${result.seriesId}" // "$baseUrl/library/${existingSeries.libraryId}/series/${result.seriesId}" manga.url = "$apiUrl/Series/${result.seriesId}" // "$baseUrl/library/${existingSeries.libraryId}/series/${result.seriesId}"
manga.artist = result.coverArtists.joinToString { it.name } manga.artist = result.coverArtists.joinToString { it.name }
manga.description = listOfNotNull( manga.description = listOfNotNull(
ratingLine.takeIf { it.isNotBlank() }, ratingLine.takeIf { it.isNotBlank() },
@@ -1077,7 +1113,6 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
} }
title = serieDto.name title = serieDto.name
thumbnail_url = "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey"
status = when (result.publicationStatus) { status = when (result.publicationStatus) {
4 -> SManga.PUBLISHING_FINISHED 4 -> SManga.PUBLISHING_FINISHED
2 -> SManga.COMPLETED 2 -> SManga.COMPLETED
@@ -1105,11 +1140,11 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
return try { return try {
val readingLists = response.parseAs<List<ReadingListDto>>() val readingLists = response.parseAs<List<ReadingListDto>>()
cachedReadingLists = readingLists cachedReadingLists = readingLists
val now = java.util.Calendar.getInstance() val now = Calendar.getInstance()
val mangaList = readingLists.map { list -> val mangaList = readingLists.map { list ->
val hasDates = list.startingYear > 0 && list.endingYear > 0 val hasDates = list.startingYear > 0 && list.endingYear > 0
val status = if (hasDates) { val status = if (hasDates) {
val endCal = java.util.Calendar.getInstance().apply { val endCal = Calendar.getInstance().apply {
set(list.endingYear, list.endingMonth - 1, 1) set(list.endingYear, list.endingMonth - 1, 1)
} }
if (now.after(endCal)) SManga.PUBLISHING_FINISHED else SManga.ONGOING if (now.after(endCal)) SManga.PUBLISHING_FINISHED else SManga.ONGOING
@@ -1284,7 +1319,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
try { try {
val seriesDto = client.newCall(GET("$apiUrl/Series/${item.seriesId}", headersBuilder().build())) val seriesDto = client.newCall(GET("$apiUrl/Series/${item.seriesId}", headersBuilder().build()))
.execute().parseAs<SeriesDto>() .execute().parseAs<SeriesDto>()
helper.createSeriesDto(seriesDto, apiUrl, baseUrl) helper.createSeriesDto(seriesDto, apiUrl, apiKey)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "Error fetching series for related reading list", e) Log.e(LOG_TAG, "Error fetching series for related reading list", e)
null null
@@ -1641,7 +1676,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
return try { return try {
val result = response.parseAs<List<SeriesDto>>() val result = response.parseAs<List<SeriesDto>>()
val mangaList = result.map { helper.createSeriesDto(it, apiUrl, baseUrl) } val mangaList = result.map { helper.createSeriesDto(it, apiUrl, apiKey) }
MangasPage(mangaList, false) MangasPage(mangaList, false)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "Error parsing search results", e) Log.e(LOG_TAG, "Error parsing search results", e)
@@ -1777,7 +1812,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.add("Authorization", "Bearer $jwtToken") .add("Authorization", "Bearer $jwtToken")
} }
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val opdsAddressPref = screen.editTextPreference( val opdsAddressPref = screen.editTextPreference(
ADDRESS_TITLE, ADDRESS_TITLE,
"OPDS url", "OPDS url",
@@ -1901,7 +1936,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
screen.addPreference(enabledFiltersPref) screen.addPreference(enabledFiltersPref)
} }
private fun androidx.preference.PreferenceScreen.editTextPreference( private fun PreferenceScreen.editTextPreference(
preKey: String, preKey: String,
title: String, title: String,
default: String, default: String,