mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-22 18:32:39 +01:00
Komga: Filter out EPUB books, fix chapter timestamps, allow setting default libraries, rename None sort to Relevance (#1282)
* Komga: Filter out EPUB books, fix chapter timestamps * Add default library setting * Make some settings not require a restart * Don't use fixed enums * Rename None sort to Relevance (#1284) * make popular default to alphabetical sort
This commit is contained in:
parent
fcd38a3ed6
commit
e571f95688
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Komga'
|
extName = 'Komga'
|
||||||
extClass = '.KomgaFactory'
|
extClass = '.KomgaFactory'
|
||||||
extVersionCode = 54
|
extVersionCode = 55
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -7,6 +7,7 @@ import android.util.Log
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.MultiSelectListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.AppInfo
|
import eu.kanade.tachiyomi.AppInfo
|
||||||
import eu.kanade.tachiyomi.extension.all.komga.KomgaUtils.addEditTextPreference
|
import eu.kanade.tachiyomi.extension.all.komga.KomgaUtils.addEditTextPreference
|
||||||
@ -50,6 +51,12 @@ import kotlin.concurrent.write
|
|||||||
|
|
||||||
open class Komga(private val suffix: String = "") : ConfigurableSource, UnmeteredSource, HttpSource() {
|
open class Komga(private val suffix: String = "") : ConfigurableSource, UnmeteredSource, HttpSource() {
|
||||||
|
|
||||||
|
internal val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val displayName by lazy { preferences.getString(PREF_DISPLAY_NAME, "")!! }
|
||||||
|
|
||||||
override val name by lazy { "Komga${displayName.ifBlank { suffix }.let { if (it.isNotBlank()) " ($it)" else "" }}" }
|
override val name by lazy { "Komga${displayName.ifBlank { suffix }.let { if (it.isNotBlank()) " ($it)" else "" }}" }
|
||||||
|
|
||||||
override val lang = "all"
|
override val lang = "all"
|
||||||
@ -65,14 +72,13 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
(0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
(0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val preferences: SharedPreferences by lazy {
|
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val displayName by lazy { preferences.getString(PREF_DISPLAY_NAME, "")!! }
|
|
||||||
private val username by lazy { preferences.getString(PREF_USERNAME, "")!! }
|
private val username by lazy { preferences.getString(PREF_USERNAME, "")!! }
|
||||||
|
|
||||||
private val password by lazy { preferences.getString(PREF_PASSWORD, "")!! }
|
private val password by lazy { preferences.getString(PREF_PASSWORD, "")!! }
|
||||||
|
|
||||||
|
private val defaultLibraries
|
||||||
|
get() = preferences.getStringSet(PREF_DEFAULT_LIBRARIES, emptySet())!!
|
||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder()
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
.set("User-Agent", "TachiyomiKomga/${AppInfo.getVersionName()}")
|
.set("User-Agent", "TachiyomiKomga/${AppInfo.getVersionName()}")
|
||||||
|
|
||||||
@ -94,7 +100,9 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
searchMangaRequest(
|
searchMangaRequest(
|
||||||
page,
|
page,
|
||||||
"",
|
"",
|
||||||
FilterList(SeriesSort()),
|
FilterList(
|
||||||
|
SeriesSort(Filter.Sort.Selection(1, true)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage =
|
override fun popularMangaParse(response: Response): MangasPage =
|
||||||
@ -104,7 +112,9 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
searchMangaRequest(
|
searchMangaRequest(
|
||||||
page,
|
page,
|
||||||
"",
|
"",
|
||||||
FilterList(SeriesSort(Filter.Sort.Selection(3, false))),
|
FilterList(
|
||||||
|
SeriesSort(Filter.Sort.Selection(3, false)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage =
|
override fun latestUpdatesParse(response: Response): MangasPage =
|
||||||
@ -124,14 +134,20 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
}
|
}
|
||||||
|
|
||||||
val url = "$baseUrl/api/v1/$type?search=$query&page=${page - 1}&deleted=false".toHttpUrl().newBuilder()
|
val url = "$baseUrl/api/v1/$type?search=$query&page=${page - 1}&deleted=false".toHttpUrl().newBuilder()
|
||||||
|
val filterList = filters.ifEmpty { getFilterList() }
|
||||||
|
|
||||||
filters.forEach { filter ->
|
if (filterList.filterIsInstance<LibraryFilter>().isEmpty()) {
|
||||||
|
url.addQueryParameter("library_id", defaultLibraries.joinToString(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
filterList.forEach { filter ->
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is UriFilter -> filter.addToUri(url)
|
is UriFilter -> filter.addToUri(url)
|
||||||
is Filter.Sort -> {
|
is Filter.Sort -> {
|
||||||
val state = filter.state ?: return@forEach
|
val state = filter.state ?: return@forEach
|
||||||
|
|
||||||
val sortCriteria = when (state.index) {
|
val sortCriteria = when (state.index) {
|
||||||
|
0 -> "relevance"
|
||||||
1 -> if (type == "series") "metadata.titleSort" else "name"
|
1 -> if (type == "series") "metadata.titleSort" else "name"
|
||||||
2 -> "createdDate"
|
2 -> "createdDate"
|
||||||
3 -> "lastModifiedDate"
|
3 -> "lastModifiedDate"
|
||||||
@ -162,9 +178,8 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val chapterNameTemplate by lazy {
|
private val chapterNameTemplate
|
||||||
preferences.getString(PREF_CHAPTER_NAME_TEMPLATE, PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)!!
|
get() = preferences.getString(PREF_CHAPTER_NAME_TEMPLATE, PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)!!
|
||||||
}
|
|
||||||
|
|
||||||
override fun getChapterUrl(chapter: SChapter) = chapter.url.replace("/api/v1/books", "/book")
|
override fun getChapterUrl(chapter: SChapter) = chapter.url.replace("/api/v1/books", "/book")
|
||||||
|
|
||||||
@ -174,18 +189,23 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val page = response.parseAs<PageWrapperDto<BookDto>>().content
|
val page = response.parseAs<PageWrapperDto<BookDto>>().content
|
||||||
val isFromReadList = response.isFromReadList()
|
val isFromReadList = response.isFromReadList()
|
||||||
val r = page.mapIndexed { index, book ->
|
val chapterNameTemplate = chapterNameTemplate
|
||||||
|
|
||||||
|
return page
|
||||||
|
.filter {
|
||||||
|
it.media.mediaProfile != "EPUB" || it.media.epubDivinaCompatible
|
||||||
|
}
|
||||||
|
.mapIndexed { index, book ->
|
||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
chapter_number = if (!isFromReadList) book.metadata.numberSort else index + 1F
|
chapter_number = if (!isFromReadList) book.metadata.numberSort else index + 1F
|
||||||
url = "$baseUrl/api/v1/books/${book.id}"
|
url = "$baseUrl/api/v1/books/${book.id}"
|
||||||
name = KomgaUtils.formatChapterName(book, chapterNameTemplate, isFromReadList)
|
name = KomgaUtils.formatChapterName(book, chapterNameTemplate, isFromReadList)
|
||||||
scanlator = book.metadata.authors.filter { it.role == "translator" }.joinToString { it.name }
|
scanlator = book.metadata.authors.filter { it.role == "translator" }.joinToString { it.name }
|
||||||
date_upload = book.metadata.releaseDate?.let { KomgaUtils.parseDate(it) }
|
date_upload = book.metadata.releaseDate?.let { KomgaUtils.parseDate(it) }
|
||||||
?: KomgaUtils.parseDateTime(book.fileLastModified)
|
?: KomgaUtils.parseDateTime(book.lastModified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sortedByDescending { it.chapter_number }
|
||||||
return r.sortedByDescending { it.chapter_number }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter) = GET("${chapter.url}/pages")
|
override fun pageListRequest(chapter: SChapter) = GET("${chapter.url}/pages")
|
||||||
@ -221,11 +241,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
UriMultiSelectFilter(
|
LibraryFilter(libraries, defaultLibraries),
|
||||||
"Libraries",
|
|
||||||
"library_id",
|
|
||||||
libraries.map { UriMultiSelectOption(it.name, it.id) },
|
|
||||||
),
|
|
||||||
UriMultiSelectFilter(
|
UriMultiSelectFilter(
|
||||||
"Status",
|
"Status",
|
||||||
"status",
|
"status",
|
||||||
@ -283,6 +299,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
default = suffix,
|
default = suffix,
|
||||||
summary = displayName.ifBlank { "Here you can change the source displayed suffix" },
|
summary = displayName.ifBlank { "Here you can change the source displayed suffix" },
|
||||||
key = PREF_DISPLAY_NAME,
|
key = PREF_DISPLAY_NAME,
|
||||||
|
restartRequired = true,
|
||||||
)
|
)
|
||||||
screen.addEditTextPreference(
|
screen.addEditTextPreference(
|
||||||
title = "Address",
|
title = "Address",
|
||||||
@ -292,12 +309,14 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
validate = { it.toHttpUrlOrNull() != null },
|
validate = { it.toHttpUrlOrNull() != null },
|
||||||
validationMessage = "The URL is invalid or malformed",
|
validationMessage = "The URL is invalid or malformed",
|
||||||
key = PREF_ADDRESS,
|
key = PREF_ADDRESS,
|
||||||
|
restartRequired = true,
|
||||||
)
|
)
|
||||||
screen.addEditTextPreference(
|
screen.addEditTextPreference(
|
||||||
title = "Username",
|
title = "Username",
|
||||||
default = "",
|
default = "",
|
||||||
summary = username.ifBlank { "The user account email" },
|
summary = username.ifBlank { "The user account email" },
|
||||||
key = PREF_USERNAME,
|
key = PREF_USERNAME,
|
||||||
|
restartRequired = true,
|
||||||
)
|
)
|
||||||
screen.addEditTextPreference(
|
screen.addEditTextPreference(
|
||||||
title = "Password",
|
title = "Password",
|
||||||
@ -305,8 +324,24 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
summary = if (password.isBlank()) "The user account password" else "*".repeat(password.length),
|
summary = if (password.isBlank()) "The user account password" else "*".repeat(password.length),
|
||||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD,
|
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD,
|
||||||
key = PREF_PASSWORD,
|
key = PREF_PASSWORD,
|
||||||
|
restartRequired = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MultiSelectListPreference(screen.context).apply {
|
||||||
|
key = PREF_DEFAULT_LIBRARIES
|
||||||
|
title = "Default libraries"
|
||||||
|
summary = buildString {
|
||||||
|
append("Show content from selected libraries by default.")
|
||||||
|
|
||||||
|
if (libraries.isEmpty()) {
|
||||||
|
append(" Browse the source to load available options.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries = libraries.map { it.name }.toTypedArray()
|
||||||
|
entryValues = libraries.map { it.id }.toTypedArray()
|
||||||
|
setDefaultValue(emptySet<String>())
|
||||||
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
EditTextPreference(screen.context).apply {
|
EditTextPreference(screen.context).apply {
|
||||||
key = PREF_CHAPTER_NAME_TEMPLATE
|
key = PREF_CHAPTER_NAME_TEMPLATE
|
||||||
title = "Chapter title format"
|
title = "Chapter title format"
|
||||||
@ -323,10 +358,6 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
|
|
||||||
setDefaultValue(PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)
|
setDefaultValue(PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
Toast.makeText(screen.context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +427,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere
|
|||||||
private const val PREF_ADDRESS = "Address"
|
private const val PREF_ADDRESS = "Address"
|
||||||
private const val PREF_USERNAME = "Username"
|
private const val PREF_USERNAME = "Username"
|
||||||
private const val PREF_PASSWORD = "Password"
|
private const val PREF_PASSWORD = "Password"
|
||||||
|
private const val PREF_DEFAULT_LIBRARIES = "Default libraries"
|
||||||
private const val PREF_CHAPTER_NAME_TEMPLATE = "Chapter name template"
|
private const val PREF_CHAPTER_NAME_TEMPLATE = "Chapter name template"
|
||||||
private const val PREF_CHAPTER_NAME_TEMPLATE_DEFAULT = "{number} - {title} ({size})"
|
private const val PREF_CHAPTER_NAME_TEMPLATE_DEFAULT = "{number} - {title} ({size})"
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.extension.all.komga
|
package eu.kanade.tachiyomi.extension.all.komga
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.extension.all.komga.dto.AuthorDto
|
import eu.kanade.tachiyomi.extension.all.komga.dto.AuthorDto
|
||||||
|
import eu.kanade.tachiyomi.extension.all.komga.dto.LibraryDto
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
@ -18,8 +19,8 @@ internal class TypeSelect : Filter.Select<String>(
|
|||||||
|
|
||||||
internal class SeriesSort(selection: Selection? = null) : Filter.Sort(
|
internal class SeriesSort(selection: Selection? = null) : Filter.Sort(
|
||||||
"Sort",
|
"Sort",
|
||||||
arrayOf("None", "Alphabetically", "Date added", "Date updated"),
|
arrayOf("Relevance", "Alphabetically", "Date added", "Date updated"),
|
||||||
selection ?: Selection(0, true),
|
selection ?: Selection(0, false),
|
||||||
)
|
)
|
||||||
|
|
||||||
internal class UnreadFilter : Filter.CheckBox("Unread", false), UriFilter {
|
internal class UnreadFilter : Filter.CheckBox("Unread", false), UriFilter {
|
||||||
@ -53,9 +54,22 @@ internal class ReadFilter : Filter.CheckBox("Read", false), UriFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class LibraryFilter(
|
||||||
|
libraries: List<LibraryDto>,
|
||||||
|
defaultLibraries: Set<String>,
|
||||||
|
) : UriMultiSelectFilter(
|
||||||
|
"Libraries",
|
||||||
|
"library_id",
|
||||||
|
libraries.map {
|
||||||
|
UriMultiSelectOption(it.name, it.id).apply {
|
||||||
|
state = defaultLibraries.contains(it.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
internal class UriMultiSelectOption(name: String, val id: String = name) : Filter.CheckBox(name, false)
|
internal class UriMultiSelectOption(name: String, val id: String = name) : Filter.CheckBox(name, false)
|
||||||
|
|
||||||
internal class UriMultiSelectFilter(
|
internal open class UriMultiSelectFilter(
|
||||||
name: String,
|
name: String,
|
||||||
private val param: String,
|
private val param: String,
|
||||||
genres: List<UriMultiSelectOption>,
|
genres: List<UriMultiSelectOption>,
|
||||||
|
@ -98,9 +98,9 @@ internal object KomgaUtils {
|
|||||||
}
|
}
|
||||||
genre = (metadata.genres + metadata.tags + booksMetadata.tags).distinct().joinToString(", ")
|
genre = (metadata.genres + metadata.tags + booksMetadata.tags).distinct().joinToString(", ")
|
||||||
description = metadata.summary.ifBlank { booksMetadata.summary }
|
description = metadata.summary.ifBlank { booksMetadata.summary }
|
||||||
booksMetadata.authors.groupBy { it.role }.let { map ->
|
booksMetadata.authors.groupBy({ it.role }, { it.name }).let { map ->
|
||||||
author = map["writer"]?.map { it.name }?.distinct()?.joinToString()
|
author = map["writer"]?.distinct()?.joinToString()
|
||||||
artist = map["penciller"]?.map { it.name }?.distinct()?.joinToString()
|
artist = map["penciller"]?.distinct()?.joinToString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +121,7 @@ internal object KomgaUtils {
|
|||||||
validate: ((String) -> Boolean)? = null,
|
validate: ((String) -> Boolean)? = null,
|
||||||
validationMessage: String? = null,
|
validationMessage: String? = null,
|
||||||
key: String = title,
|
key: String = title,
|
||||||
|
restartRequired: Boolean = false,
|
||||||
) {
|
) {
|
||||||
EditTextPreference(context).apply {
|
EditTextPreference(context).apply {
|
||||||
this.key = key
|
this.key = key
|
||||||
@ -159,8 +160,13 @@ internal object KomgaUtils {
|
|||||||
|
|
||||||
setOnPreferenceChangeListener { _, _ ->
|
setOnPreferenceChangeListener { _, _ ->
|
||||||
try {
|
try {
|
||||||
|
val result = text.isBlank() || validate?.invoke(text) ?: true
|
||||||
|
|
||||||
|
if (restartRequired && result) {
|
||||||
Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show()
|
Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show()
|
||||||
text.isBlank() || validate?.invoke(text) ?: true
|
}
|
||||||
|
|
||||||
|
result
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
false
|
false
|
||||||
|
@ -78,6 +78,8 @@ data class MediaDto(
|
|||||||
val status: String,
|
val status: String,
|
||||||
val mediaType: String,
|
val mediaType: String,
|
||||||
val pagesCount: Int,
|
val pagesCount: Int,
|
||||||
|
val mediaProfile: String = "DIVINA",
|
||||||
|
val epubDivinaCompatible: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
Loading…
Reference in New Issue
Block a user