Add Kavita extension (#10241)

* Trying out git
Sorry in advance if this messes up.
Trying to work on my extensions fork to develop a new extension

So far Mangas are listed Successfully.

* Added Kavita Icons

* Added Kavita ParseDetails. All data is retrieved the first time SManga is created, but reimplemented since it retrieves info from backup

* Added Chapter List

      "chapters": [
        {
          "id": 750,
          "range": "1.1",
          "number": "11",
          "pages": 16,
          "isSpecial": false,

        },
        {
          "id": 767,
          "range": "001.jpg",
          "number": "0",
          "pages": 5,
          "isSpecial": true,

        },
        {
          "id": 818,
          "range": "1.3",
          "number": "13",
          "pages": 8,
          "isSpecial": false,

        },
        {
          "id": 817,
          "range": "1.4",
          "number": "14",
          "pages": 12,
          "isSpecial": false,

        },
        {
          "id": 768,
          "range": "002.jpg",
          "number": "0",
          "pages": 5,
          "isSpecial": true,

        },
<- extracted json
see how when isSpecial, number = 0
Needs to be fixed

* Delete .github/ISSUE_TEMPLATE directory

* Checklist:
 ✓ It Lists the library
 ✓ It Lists the chapters of a serie
 ✓ Clicking on a chapter shows the image

* New Icons & login finished
Added token isvalid else relogins
Login should be confirmed of not having problems

* Changed Defaults to Kavita Wiki's

* Added Search Manga Feature

* Removed User Authentication. Leaving only token Behind

* Refactored out login credentials and rewrote the authentication for joining. user now just puts in base path and api key

* Cleanup some of the code and re-arange to keep related code together.

* Cleanup some of the code and re-arange to keep related code together.

* Still WIP, stream lined creation of Series and Series detail. Chapters are a bit broken.

* Series generation with metadata is done. Working on chapter code.

* Code to properly label chapters is done

* Revert "Delete .github/ISSUE_TEMPLATE directory"

This reverts commit 8d5740c4

* Reset editor Config

* Removed Input token

* Cleaned Unused code and files

* Readded Search
Removed token again

* Modified all imports to avoid lintcheck errors -> deleted all wildcards

* Integrated format filter with latest backend code

* Refactored authentication to now grab it from solely the address field.

* Refactored authentication to now grab it from solely the address field.

* Refactored authentication to now grab it from solely the address field. Auth token is fetched when needed without requiring user to restart their session.

* - Fixed duped Pages on main
- Fixed Chapters going backwards
- Fixed last chapter getting duped

* - Fixed login with new opds url setting.

* Cleaned commented code

* Cleaned commented code

* Added isLogin. IOException if invalid apikey

* Fixes Volume Naming

Previously All Volume Number was displayed as '0', It fixes that bug.

* Removed Gson usages

* Updated the filter Dto for all methods

* Merged Http err Handling

* Added debug option to see 400 error

* 1.2.3. Commented debugRequest

* Crappy code warning: Added first filtering support

* Manga details fixed:
Artist,Author,Summary, ¿genres? should be displayed properly now

* Pushing nothing changed

* - Fixed 500 error when reset filter
- Added reading status filter
- Cleaned some spaghetti code that was redundant

* Code cleanup

* Format Filter is almost readt. Majora finish it.

* Added library filter

* More code cleanup. Authentication request only sent once.

* People filter Added

* Added collection,character and translator filters
Remaining filters:
- Rating
- Format

* Fixed displayed chapter number.
Cleaned chapter title.

* - Fixed #12
- Reworked Login
- Added Logs
- Added more errors handlers

* Added format filter
Added check for url (must start with http/s)
Fixed filters getting disabled when scrolling further (request for new pages did not have filters)
Code cleanup

* updated extVersionCode ->5 - ready for release

* updated extVersionCode ->6 - pull request to tachiyomiorg/ext

* removed release folder from git and removed unwanted modifications in the project

* removed release folder from git and removed unwanted modifications in the project

* unwanted modification

* Update src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt

Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>

* Update src/all/kavita/build.gradle

Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>

* removed unused import

* Changed icons to be more consistent with other extensions.

* QuickFix: format filter wrong default.

* Added webview support (Needs to be tested thoroughly)
Refactored baseUrl to apiUrl
baseURl is now only "Server:Port" (without  trailing slashes.)
Closes #14

* Fixed Json Decode error when genres,people or artists are empty.
Solution to #15 is to use tachiyomi `sort by chapter number` feature.

* Added publication status filter

* Added sort filter

* Bump extVersionCode

* Updated cover artist serial name in SeriesMetadataDto

* Added user rating filter

* Removed other people filter

* Removed other people filter
Fixed filtering suddenly not applying

* Filters that are not populated won't show

* Added new preferences options to customize filters showing in filter list

* Added more configurable sources to have multipe kavita instances

* Code Cleanup

* Fixed ReadStatus

* Changed order of preference setting

* Added more checks and log exceptions
v0.5+ is required message added

* Added more checks and log exceptions
v0.5+ is required message added

* Fixed Cover Artist filter preference not working

* Changed extVersionCode to 1

* Fixed Tags filter not populating

* Added toast to restart app when source name is changed

* Forgot to stage this file. Fixes tags filter not populated

* Added one more error message. v0.5+ required

* Fixed having to restart twice the app after preference changes
Changed setupLogin order
Fixed some log messages.

* Changed more log messages
Some cleanup.

* Fixed Unexpected JSON token in manga details

* Fixed search was creating a Smanga with url = series ID instead of complete url

* Fixed search was creating a Smanga with url = series ID instead of complete url

Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
Co-authored-by: Shashank Pujari <shashank-p@users.noreply.github.com>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
This commit is contained in:
ThePromidius 2022-01-24 11:23:34 +01:00 committed by GitHub
parent f1a9fe6d16
commit 59380ed6f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1638 additions and 0 deletions

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -0,0 +1,16 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Kavita'
pkgNameSuffix = 'all.kavita'
extClass = '.KavitaFactory'
extVersionCode = 1
}
dependencies {
implementation 'info.debatty:java-string-similarity:2.0.0'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
package eu.kanade.tachiyomi.extension.all.kavita
object KavitaConstants {
// togle filters
const val toggledFiltersPref = "toggledFilters"
val filterPrefEntries = arrayOf(
"Sort Options",
"Format",
"Libraries",
"Read Status",
"Genres",
"Tags",
"Collections",
"Languages",
"Publication Status",
"Rating",
"Age Rating",
"Writers",
"Penciller",
"Inker",
"Colorist",
"Letterer",
"Cover Artist",
"Editor",
"Publisher",
"Character",
"Translators"
)
val filterPrefEntriesValue = arrayOf(
"Sort Options",
"Format",
"Libraries",
"Read Status",
"Genres",
"Tags",
"Collections",
"Languages",
"Publication Status",
"Rating",
"Age Rating",
"Writers",
"Penciller",
"Inker",
"Colorist",
"Letterer",
"CoverArtist",
"Editor",
"Publisher",
"Character",
"Translators"
)
val defaultFilterPrefEntries = setOf(
"Sort Options",
"Format",
"Libraries",
"Read Status",
"Genres",
"Tags",
"Collections",
"Languages",
"Publication Status",
"Rating",
"Age Rating",
"Writers",
"Penciller",
"Inker",
"Colorist",
"Letterer",
"CoverArtist",
"Editor",
"Publisher",
"Character",
"Translators"
)
const val customSourceNamePref = "customSourceName"
}

View File

@ -0,0 +1,13 @@
package eu.kanade.tachiyomi.extension.all.kavita
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class KavitaFactory : SourceFactory {
override fun createSources(): List<Source> =
listOf(
Kavita("1"),
Kavita("2"),
Kavita("3")
)
}

View File

@ -0,0 +1,48 @@
package eu.kanade.tachiyomi.extension.all.kavita
import eu.kanade.tachiyomi.extension.all.kavita.dto.PaginationInfo
import eu.kanade.tachiyomi.extension.all.kavita.dto.SeriesDto
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
class KavitaHelper {
val json = Json {
isLenient = true
ignoreUnknownKeys = true
allowSpecialFloatingPointValues = true
useArrayPolymorphism = true
prettyPrint = true
}
val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSS", Locale.US)
.apply { timeZone = TimeZone.getTimeZone("UTC") }
fun parseDate(dateAsString: String): Long =
dateFormatter.parse(dateAsString)?.time ?: 0
fun hasNextPage(response: Response): Boolean {
val paginationHeader = response.header("Pagination")
var hasNextPage = false
if (!paginationHeader.isNullOrEmpty()) {
val paginationInfo = json.decodeFromString<PaginationInfo>(paginationHeader)
hasNextPage = paginationInfo.currentPage + 1 > paginationInfo.totalPages
}
return !hasNextPage
}
fun getIdFromUrl(url: String): Int {
return url.split("/").last().toInt()
}
fun createSeriesDto(obj: SeriesDto, baseUrl: String): SManga =
SManga.create().apply {
url = "$baseUrl/Series/${obj.id}"
title = obj.name
// Deprecated: description = obj.summary
thumbnail_url = "$baseUrl/image/series-cover?seriesId=${obj.id}"
}
}

View File

@ -0,0 +1,110 @@
package eu.kanade.tachiyomi.extension.all.kavita.dto
import kotlinx.serialization.Serializable
@Serializable
enum class MangaFormat(val format: Int) {
Image(0),
Archive(1),
Unknown(2),
Epub(3),
Pdf(4);
companion object {
private val map = PersonRole.values().associateBy(PersonRole::role)
fun fromInt(type: Int) = map[type]
}
}
enum class PersonRole(val role: Int) {
Other(1),
Writer(3),
Penciller(4),
Inker(5),
Colorist(6),
Letterer(7),
CoverArtist(8),
Editor(9),
Publisher(10),
Character(11),
Translator(12);
companion object {
private val map = PersonRole.values().associateBy(PersonRole::role)
fun fromInt(type: Int) = map[type]
}
}
@Serializable
data class SeriesDto(
val id: Int,
val name: String,
val originalName: String = "",
val thumbnail_url: String? = "",
val localizedName: String? = "",
val sortName: String? = "",
val pages: Int,
val coverImageLocked: Boolean = true,
val pagesRead: Int,
val userRating: Int,
val userReview: String? = "",
val format: Int,
val created: String? = "",
val libraryId: Int,
val libraryName: String? = ""
)
@Serializable
data class SeriesMetadataDto(
val id: Int,
val summary: String? = "",
val writers: List<Person> = emptyList(),
val coverArtists: List<Person> = emptyList(),
val genres: List<Genres> = emptyList(),
val seriesId: Int,
val ageRating: Int
)
@Serializable
data class Genres(
val title: String
)
@Serializable
data class Person(
val name: String
)
@Serializable
data class VolumeDto(
val id: Int,
val number: Int,
val name: String,
val pages: Int,
val pagesRead: Int,
val lastModified: String,
val created: String,
val seriesId: Int,
val chapters: List<ChapterDto> = emptyList()
)
@Serializable
data class ChapterDto(
val id: Int,
val range: String,
val number: String,
val pages: Int,
val isSpecial: Boolean,
val title: String,
val pagesRead: Int,
val coverImageLocked: Boolean,
val volumeId: Int,
val created: String
)
@Serializable
data class KavitaComicsSearch(
val seriesId: Int,
val name: String,
val originalName: String,
val sortName: String,
val localizedName: String,
val format: Int,
val libraryName: String,
val libraryId: Int
)

View File

@ -0,0 +1,76 @@
package eu.kanade.tachiyomi.extension.all.kavita.dto
import kotlinx.serialization.Serializable
/**
* This file contains all class for filtering
* */
@Serializable
data class MetadataGenres(
val id: Int,
val title: String,
)
@Serializable
data class MetadataPeople(
val id: Int,
val name: String,
val role: Int
)
@Serializable
data class MetadataPubStatus(
val value: Int,
val title: String
)
@Serializable
data class MetadataTag(
val id: Int,
val title: String,
)
@Serializable
data class MetadataAgeRatings(
val value: Int,
val title: String
)
@Serializable
data class MetadataLanguages(
val isoCode: String,
val title: String
)
@Serializable
data class MetadataLibrary(
val id: Int,
val name: String,
val type: Int
)
@Serializable
data class MetadataCollections(
val id: Int,
val title: String,
)
data class MetadataPayload(
var sorting: Int = 1,
var sorting_asc: Boolean = true,
var readStatus: ArrayList<String> = arrayListOf< String>(),
var genres: ArrayList<Int> = arrayListOf<Int>(),
var tags: ArrayList<Int> = arrayListOf<Int>(),
var ageRating: ArrayList<Int> = arrayListOf<Int>(),
var formats: ArrayList<Int> = arrayListOf<Int>(),
var collections: ArrayList<Int> = arrayListOf<Int>(),
var userRating: Int = 0,
var people: ArrayList<Int> = arrayListOf<Int>(),
var language: ArrayList<String> = arrayListOf<String>(),
var libraries: ArrayList<Int> = arrayListOf<Int>(),
var pubStatus: ArrayList<Int> = arrayListOf<Int>(),
var peopleWriters: ArrayList<Int> = arrayListOf<Int>(),
var peoplePenciller: ArrayList<Int> = arrayListOf<Int>(),
var peopleInker: ArrayList<Int> = arrayListOf<Int>(),
var peoplePeoplecolorist: ArrayList<Int> = arrayListOf<Int>(),
var peopleLetterer: ArrayList<Int> = arrayListOf<Int>(),
var peopleCoverArtist: ArrayList<Int> = arrayListOf<Int>(),
var peopleEditor: ArrayList<Int> = arrayListOf<Int>(),
var peoplePublisher: ArrayList<Int> = arrayListOf<Int>(),
var peopleCharacter: ArrayList<Int> = arrayListOf<Int>(),
var peopleTranslator: ArrayList<Int> = arrayListOf<Int>(),
)

View File

@ -0,0 +1,18 @@
package eu.kanade.tachiyomi.extension.all.kavita.dto
import kotlinx.serialization.Serializable
@Serializable // Used to process login
data class AuthenticationDto(
val username: String,
val token: String,
val apiKey: String
)
@Serializable
data class PaginationInfo(
val currentPage: Int,
val itemsPerPage: Int,
val totalItems: Int,
val totalPages: Int
)