Skip to content

Commit be8baf9

Browse files
authored
Kagane: Update API Endpoint (#13353)
* fix metadata fetching. Need works on filters and DRM challenge * Pass the DRM Challenge, need works on image handling * fix the image handling, filter still need to be worked on * Merging changes from d-brox * Fix source's first load speed being too slow * chore cleaning unused file, function, etc * fix Smanga artist mapping * chore forgot to delete ImageInterceptor * fix cloudflare initial check and refresh token issue * Change tags input from checkboxes with text field * chore for logs, refactoring Smanga, and adjustment in searchMangaParse
1 parent ed910ae commit be8baf9

7 files changed

Lines changed: 452 additions & 718 deletions

File tree

src/en/kagane/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
ext {
22
extName = 'Kagane'
33
extClass = '.Kagane'
4-
extVersionCode = 16
4+
extVersionCode = 17
55
isNsfw = true
66
}
77

src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Dto.kt

Lines changed: 165 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,110 +8,212 @@ import kotlinx.serialization.Serializable
88
import java.text.SimpleDateFormat
99
import java.util.Locale
1010

11+
@Serializable
12+
class GenreDto(
13+
val id: String,
14+
@SerialName("genre_name")
15+
val genreName: String,
16+
)
17+
18+
@Serializable
19+
class TagDto(
20+
val id: String,
21+
@SerialName("tag_name")
22+
val tagName: String,
23+
)
24+
25+
@Serializable
26+
class SourcesDto(
27+
val sources: List<SourceDto>,
28+
)
29+
30+
@Serializable
31+
class SourceDto(
32+
@SerialName("source_id")
33+
val sourceId: String,
34+
val title: String,
35+
@SerialName("source_type")
36+
val sourceType: String,
37+
)
38+
1139
@Serializable
1240
class SearchDto(
13-
val content: List<Book>,
14-
val last: Boolean,
41+
val content: List<Book> = emptyList(),
42+
val last: Boolean = true,
43+
@SerialName("total_elements")
44+
val totalElements: Int = 0,
45+
@SerialName("total_pages")
46+
val totalPages: Int = 0,
1547
) {
1648
fun hasNextPage() = !last
1749

1850
@Serializable
1951
class Book(
52+
@SerialName("series_id")
2053
val id: String,
21-
val name: String,
22-
val source: String,
23-
@SerialName("books_count")
54+
val title: String,
55+
@SerialName("source_id")
56+
val sourceId: String? = null,
57+
@SerialName("current_books")
2458
val booksCount: Int,
25-
@SerialName("release_date")
26-
val releaseDate: String?,
59+
@SerialName("start_year")
60+
val startYear: Int? = null,
61+
@SerialName("cover_image_id")
62+
val coverImage: String? = null,
63+
@SerialName("alternate_titles")
64+
val alternateTitles: List<String> = emptyList(),
2765
) {
2866

29-
fun toSManga(domain: String, showSource: Boolean): SManga = SManga.create().apply {
30-
title = if (showSource) "${name.trim()} [$source]" else name
67+
fun toSManga(domain: String, showSource: Boolean, sources: Map<String, String>): SManga = SManga.create().apply {
68+
title = if (showSource) "${this@Book.title.trim()} [${sources[this@Book.sourceId]}]" else this@Book.title.trim()
3169
url = id
32-
thumbnail_url = "$domain/api/v1/series/$id/thumbnail"
70+
thumbnail_url = coverImage?.let { "$domain/api/v2/image/$it" }
3371
}
3472
}
3573
}
3674

3775
@Serializable
3876
class AlternateSeries(
39-
@SerialName("books_count")
77+
@SerialName("current_books")
4078
val booksCount: Int,
41-
@SerialName("release_date")
42-
val releaseDate: String?,
79+
@SerialName("start_year")
80+
val startYear: Int? = null,
4381
)
4482

4583
@Serializable
4684
class DetailsDto(
47-
val source: String,
48-
val authors: List<String>,
49-
val status: String,
50-
val summary: String?,
51-
val genres: List<String>,
52-
@SerialName("alternate_titles")
53-
val alternateTitles: List<AlternateTitles>,
85+
val title: String,
86+
val description: String?,
87+
@SerialName("publication_status")
88+
val publicationStatus: String,
89+
@SerialName("upload_status")
90+
val uploadStatus: String,
91+
val format: String?,
92+
@SerialName("source_id")
93+
val sourceId: String?,
94+
@SerialName("series_staff")
95+
val seriesStaff: List<SeriesStaff> = emptyList(),
96+
val genres: List<Genre> = emptyList(),
97+
val tags: List<Tag> = emptyList(),
98+
@SerialName("series_alternate_titles")
99+
val seriesAlternateTitles: List<AlternateTitle> = emptyList(),
100+
@SerialName("series_books")
101+
val seriesBooks: List<ChapterDto.Book> = emptyList(),
54102
) {
55103
@Serializable
56-
class AlternateTitles(
104+
class SeriesStaff(
105+
val name: String,
106+
val role: String,
107+
)
108+
109+
@Serializable
110+
class Genre(
111+
@SerialName("genre_name")
112+
val genreName: String,
113+
)
114+
115+
@Serializable
116+
class Tag(
117+
@SerialName("tag_name")
118+
val tagName: String,
119+
)
120+
121+
@Serializable
122+
class AlternateTitle(
57123
val title: String,
124+
val label: String?,
58125
)
59126

60-
fun toSManga(): SManga = SManga.create().apply {
127+
fun toSManga(sourceName: String? = null): SManga = SManga.create().apply {
61128
val desc = StringBuilder()
62-
if (!summary.isNullOrBlank()) desc.append(summary + "\n\n")
63-
desc.append("Source: ").append(source + "\n\n")
64129

65-
if (alternateTitles.isNotEmpty()) {
66-
desc.append("Associated Name(s):\n\n")
67-
alternateTitles.forEach { desc.append("${it.title}\n") }
130+
// Add main description
131+
this@DetailsDto.description?.takeIf { it.isNotBlank() }?.let {
132+
desc.append(it.trim())
133+
desc.append("\n")
134+
}
135+
136+
// Add alternate titles at the end
137+
if (seriesAlternateTitles.isNotEmpty()) {
138+
if (desc.isNotEmpty()) desc.append("\n")
139+
desc.append("Associated Name(s):\n")
140+
seriesAlternateTitles.forEach {
141+
desc.append("${it.title}\n")
142+
}
143+
}
144+
145+
// Extract authors and artists from staff (roles like "Author", "Artist", "Story", "Art")
146+
val authors = seriesStaff.filter {
147+
it.role.contains("Author", ignoreCase = true) || it.role.contains("Story", ignoreCase = true)
148+
}.map { it.name }.distinct()
149+
val artists = seriesStaff.filter {
150+
it.role.contains("Artist", ignoreCase = true) || it.role.contains("Art", ignoreCase = true)
68151
}
152+
.map { it.name }
153+
.distinct()
154+
.joinToString(", ")
69155

156+
artist = artists
70157
author = authors.joinToString()
71-
description = desc.toString()
72-
genre = (listOf(source) + genres).joinToString()
73-
status = this@DetailsDto.status.toStatus()
158+
description = desc.toString().trim()
159+
genre = genres.joinToString { it.genreName }
160+
status = this@DetailsDto.publicationStatus.toStatus()
74161
}
75162

76-
private fun String.toStatus(): Int {
77-
return when (this) {
78-
"ONGOING" -> SManga.ONGOING
79-
"ENDED" -> SManga.COMPLETED
80-
"HIATUS" -> SManga.ON_HIATUS
81-
else -> SManga.UNKNOWN
82-
}
163+
private fun String.toStatus(): Int = when (this.uppercase()) {
164+
"ONGOING" -> SManga.ONGOING
165+
"COMPLETED" -> SManga.COMPLETED
166+
"HIATUS" -> SManga.ON_HIATUS
167+
"CANCELLED" -> SManga.CANCELLED
168+
else -> SManga.UNKNOWN
83169
}
84170
}
85171

86172
@Serializable
87173
class ChapterDto(
88-
val content: List<Book>,
174+
@SerialName("series_books")
175+
val seriesBooks: List<Book>,
89176
) {
90177
@Serializable
91178
class Book(
179+
@SerialName("book_id")
92180
val id: String,
93181
@SerialName("series_id")
94-
val seriesId: String,
182+
val seriesId: String? = null,
95183
val title: String,
96-
@SerialName("release_date")
97-
val releaseDate: String?,
98-
@SerialName("pages_count")
184+
@SerialName("created_at")
185+
val createdAt: String?,
186+
@SerialName("page_count")
99187
val pagesCount: Int,
100-
@SerialName("number_sort")
188+
@SerialName("sort_no")
101189
val number: Float,
190+
@SerialName("chapter_no")
191+
val chapterNo: String?,
192+
@SerialName("volume_no")
193+
val volumeNo: String?,
102194
) {
103-
fun toSChapter(useSourceChapterNumber: Boolean = false): SChapter = SChapter.create().apply {
104-
url = "$seriesId;$id;$pagesCount"
105-
name = title
106-
date_upload = dateFormat.tryParse(releaseDate)
195+
fun toSChapter(actualSeriesId: String, useSourceChapterNumber: Boolean = false): SChapter = SChapter.create().apply {
196+
url = "/series/$actualSeriesId/reader/$id"
197+
name = buildChapterName()
198+
date_upload = dateFormat.tryParse(createdAt)
107199
if (useSourceChapterNumber) {
108200
chapter_number = number
109201
}
110202
}
203+
204+
private fun buildChapterName(): String = if (!chapterNo.isNullOrBlank()) {
205+
if (title.isNotBlank()) {
206+
"Chapter $chapterNo: $title"
207+
} else {
208+
"Chapter $chapterNo"
209+
}
210+
} else {
211+
title
212+
}
111213
}
112214

113215
companion object {
114-
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
216+
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH)
115217
}
116218
}
117219

@@ -121,6 +223,19 @@ class ChallengeDto(
121223
val accessToken: String,
122224
@SerialName("cache_url")
123225
val cacheUrl: String,
124-
@SerialName("page_mapping")
125-
val pageMapping: Map<Int, String>,
226+
val pages: List<PageDto>,
227+
)
228+
229+
@Serializable
230+
class PageDto(
231+
@SerialName("page_number")
232+
val pageNumber: Int,
233+
@SerialName("page_uuid")
234+
val pageUuid: String,
235+
)
236+
237+
@Serializable
238+
class IntegrityDto(
239+
val token: String,
240+
val exp: Long,
126241
)

0 commit comments

Comments
 (0)