diff --git a/src/all/komga/build.gradle b/src/all/komga/build.gradle index 1d6cc3742..e4a56a23f 100644 --- a/src/all/komga/build.gradle +++ b/src/all/komga/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Komga' extClass = '.KomgaFactory' - extVersionCode = 54 + extVersionCode = 55 } apply from: "$rootDir/common.gradle" diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt index 87627588b..f49627c4a 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt @@ -7,6 +7,7 @@ import android.util.Log import android.widget.Toast import androidx.preference.EditTextPreference import androidx.preference.ListPreference +import androidx.preference.MultiSelectListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.AppInfo 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() { + internal val preferences: SharedPreferences by lazy { + Injekt.get().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 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 } - internal val preferences: SharedPreferences by lazy { - Injekt.get().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 password by lazy { preferences.getString(PREF_PASSWORD, "")!! } + private val defaultLibraries + get() = preferences.getStringSet(PREF_DEFAULT_LIBRARIES, emptySet())!! + override fun headersBuilder() = super.headersBuilder() .set("User-Agent", "TachiyomiKomga/${AppInfo.getVersionName()}") @@ -94,7 +100,9 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere searchMangaRequest( page, "", - FilterList(SeriesSort()), + FilterList( + SeriesSort(Filter.Sort.Selection(1, true)), + ), ) override fun popularMangaParse(response: Response): MangasPage = @@ -104,7 +112,9 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere searchMangaRequest( page, "", - FilterList(SeriesSort(Filter.Sort.Selection(3, false))), + FilterList( + SeriesSort(Filter.Sort.Selection(3, false)), + ), ) 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 filterList = filters.ifEmpty { getFilterList() } - filters.forEach { filter -> + if (filterList.filterIsInstance().isEmpty()) { + url.addQueryParameter("library_id", defaultLibraries.joinToString(",")) + } + + filterList.forEach { filter -> when (filter) { is UriFilter -> filter.addToUri(url) is Filter.Sort -> { val state = filter.state ?: return@forEach val sortCriteria = when (state.index) { + 0 -> "relevance" 1 -> if (type == "series") "metadata.titleSort" else "name" 2 -> "createdDate" 3 -> "lastModifiedDate" @@ -162,9 +178,8 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere } } - private val chapterNameTemplate by lazy { - preferences.getString(PREF_CHAPTER_NAME_TEMPLATE, PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)!! - } + private val chapterNameTemplate + get() = preferences.getString(PREF_CHAPTER_NAME_TEMPLATE, PREF_CHAPTER_NAME_TEMPLATE_DEFAULT)!! 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 { val page = response.parseAs>().content val isFromReadList = response.isFromReadList() - val r = page.mapIndexed { index, book -> - SChapter.create().apply { - chapter_number = if (!isFromReadList) book.metadata.numberSort else index + 1F - url = "$baseUrl/api/v1/books/${book.id}" - name = KomgaUtils.formatChapterName(book, chapterNameTemplate, isFromReadList) - scanlator = book.metadata.authors.filter { it.role == "translator" }.joinToString { it.name } - date_upload = book.metadata.releaseDate?.let { KomgaUtils.parseDate(it) } - ?: KomgaUtils.parseDateTime(book.fileLastModified) - } - } + val chapterNameTemplate = chapterNameTemplate - return r.sortedByDescending { it.chapter_number } + return page + .filter { + it.media.mediaProfile != "EPUB" || it.media.epubDivinaCompatible + } + .mapIndexed { index, book -> + SChapter.create().apply { + chapter_number = if (!isFromReadList) book.metadata.numberSort else index + 1F + url = "$baseUrl/api/v1/books/${book.id}" + name = KomgaUtils.formatChapterName(book, chapterNameTemplate, isFromReadList) + scanlator = book.metadata.authors.filter { it.role == "translator" }.joinToString { it.name } + date_upload = book.metadata.releaseDate?.let { KomgaUtils.parseDate(it) } + ?: KomgaUtils.parseDateTime(book.lastModified) + } + } + .sortedByDescending { it.chapter_number } } override fun pageListRequest(chapter: SChapter) = GET("${chapter.url}/pages") @@ -221,11 +241,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere } }, ), - UriMultiSelectFilter( - "Libraries", - "library_id", - libraries.map { UriMultiSelectOption(it.name, it.id) }, - ), + LibraryFilter(libraries, defaultLibraries), UriMultiSelectFilter( "Status", "status", @@ -283,6 +299,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere default = suffix, summary = displayName.ifBlank { "Here you can change the source displayed suffix" }, key = PREF_DISPLAY_NAME, + restartRequired = true, ) screen.addEditTextPreference( title = "Address", @@ -292,12 +309,14 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere validate = { it.toHttpUrlOrNull() != null }, validationMessage = "The URL is invalid or malformed", key = PREF_ADDRESS, + restartRequired = true, ) screen.addEditTextPreference( title = "Username", default = "", summary = username.ifBlank { "The user account email" }, key = PREF_USERNAME, + restartRequired = true, ) screen.addEditTextPreference( 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), inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_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()) + }.also(screen::addPreference) + EditTextPreference(screen.context).apply { key = PREF_CHAPTER_NAME_TEMPLATE title = "Chapter title format" @@ -323,10 +358,6 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere """.trimMargin() 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) } @@ -396,6 +427,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere private const val PREF_ADDRESS = "Address" private const val PREF_USERNAME = "Username" 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_DEFAULT = "{number} - {title} ({size})" diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaFilters.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaFilters.kt index 54f1bb7c8..8803775b6 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaFilters.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaFilters.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.extension.all.komga 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 okhttp3.HttpUrl @@ -18,8 +19,8 @@ internal class TypeSelect : Filter.Select( internal class SeriesSort(selection: Selection? = null) : Filter.Sort( "Sort", - arrayOf("None", "Alphabetically", "Date added", "Date updated"), - selection ?: Selection(0, true), + arrayOf("Relevance", "Alphabetically", "Date added", "Date updated"), + selection ?: Selection(0, false), ) 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, + defaultLibraries: Set, +) : 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 UriMultiSelectFilter( +internal open class UriMultiSelectFilter( name: String, private val param: String, genres: List, diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaUtils.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaUtils.kt index da3c3d54b..def2dbecd 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaUtils.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/KomgaUtils.kt @@ -98,9 +98,9 @@ internal object KomgaUtils { } genre = (metadata.genres + metadata.tags + booksMetadata.tags).distinct().joinToString(", ") description = metadata.summary.ifBlank { booksMetadata.summary } - booksMetadata.authors.groupBy { it.role }.let { map -> - author = map["writer"]?.map { it.name }?.distinct()?.joinToString() - artist = map["penciller"]?.map { it.name }?.distinct()?.joinToString() + booksMetadata.authors.groupBy({ it.role }, { it.name }).let { map -> + author = map["writer"]?.distinct()?.joinToString() + artist = map["penciller"]?.distinct()?.joinToString() } } @@ -121,6 +121,7 @@ internal object KomgaUtils { validate: ((String) -> Boolean)? = null, validationMessage: String? = null, key: String = title, + restartRequired: Boolean = false, ) { EditTextPreference(context).apply { this.key = key @@ -159,8 +160,13 @@ internal object KomgaUtils { setOnPreferenceChangeListener { _, _ -> try { - Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show() - text.isBlank() || validate?.invoke(text) ?: true + val result = text.isBlank() || validate?.invoke(text) ?: true + + if (restartRequired && result) { + Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show() + } + + result } catch (e: Exception) { e.printStackTrace() false diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt index f29e1dac4..a24ca91ba 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt @@ -78,6 +78,8 @@ data class MediaDto( val status: String, val mediaType: String, val pagesCount: Int, + val mediaProfile: String = "DIVINA", + val epubDivinaCompatible: Boolean = false, ) @Serializable