Skip to content

Commit 37036f5

Browse files
author
RiskyN
committed
feat: fix search TvType, add Movies/OVA rails, fix genre/studio tags from catalog cache
1 parent 56df21c commit 37036f5

1 file changed

Lines changed: 54 additions & 15 deletions

File tree

AnimeVerse/src/main/kotlin/com/animeverse/AnimeVerseProvider.kt

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,21 @@ class AnimeVerseProvider : MainAPI() {
145145
if (queryWords.isEmpty()) return emptyList()
146146

147147
return items.filter { item ->
148-
val title = item.title.lowercase()
149-
val altTitle = item.alternativeTitle?.lowercase() ?: ""
148+
val title = item.title.lowercase()
149+
val altTitle = item.alternativeTitle?.lowercase() ?: ""
150150
val searchTitle = item.searchTitle?.lowercase() ?: ""
151151
queryWords.all { word ->
152152
title.contains(word) || altTitle.contains(word) || searchTitle.contains(word)
153153
}
154154
}.map { item ->
155-
val isDub = item.slug.contains("-dub", ignoreCase = true) ||
156-
item.title.contains("(dub)", ignoreCase = true)
157-
newAnimeSearchResponse(item.title, item.slug) {
155+
val isDub = item.slug.contains("-dub", ignoreCase = true) ||
156+
item.title.contains("(dub)", ignoreCase = true)
157+
// Map content type to correct Cloudstream TvType for proper UI category icons
158+
val tvType = when (item.type?.uppercase()) {
159+
"MOVIE" -> TvType.AnimeMovie
160+
else -> TvType.Anime
161+
}
162+
newAnimeSearchResponse(item.title, item.slug, tvType) {
158163
this.posterUrl = if (item.cover.startsWith("/i/")) "$mainUrl${item.cover}" else item.cover
159164
this.dubStatus = if (isDub) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed)
160165
}
@@ -165,11 +170,14 @@ class AnimeVerseProvider : MainAPI() {
165170
// just-added is the fastest + most reliable endpoint (~1s), load it first
166171
Pair("just-added", "Just Added"),
167172
Pair("recent", "Recent Episodes"),
168-
Pair("trending", "Trending This Week")
173+
Pair("trending", "Trending This Week"),
174+
Pair("movies", "Anime Movies"),
175+
Pair("ova", "OVA & Specials")
169176
)
170177

171178
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
172179
var hasMore = false
180+
val pageSize = 20
173181

174182
val animeList: List<SearchResponse> = when (request.data) {
175183
"just-added" -> {
@@ -184,11 +192,11 @@ class AnimeVerseProvider : MainAPI() {
184192
}
185193
}
186194
"recent" -> {
187-
// No pagination — single page of 10 latest episode updates
195+
// No pagination — single page of latest episode updates
188196
val jsonText = signedGet("/api/v1/recent")
189197
val items = mapper.readValue(jsonText, RecentResponse::class.java).items
190198
items.map { item ->
191-
newAnimeSearchResponse("${item.seriesTitle} - Episode ${item.number}", item.seriesSlug) {
199+
newAnimeSearchResponse("${item.seriesTitle} · Ep ${item.number}", item.seriesSlug) {
192200
this.posterUrl = getPosterUrl(item.seriesId)
193201
}
194202
}
@@ -203,6 +211,31 @@ class AnimeVerseProvider : MainAPI() {
203211
}
204212
}
205213
}
214+
"movies" -> {
215+
// Filter catalog locally by type == Movie; paginate with pageSize items per page
216+
val all = getCatalog().filter { it.type?.uppercase() == "MOVIE" }
217+
val start = (page - 1) * pageSize
218+
val slice = if (start < all.size) all.subList(start, minOf(start + pageSize, all.size)) else emptyList()
219+
hasMore = (start + pageSize) < all.size
220+
slice.map { item ->
221+
newAnimeSearchResponse(item.title, item.slug, TvType.AnimeMovie) {
222+
this.posterUrl = if (item.cover.startsWith("/i/")) "$mainUrl${item.cover}" else item.cover
223+
}
224+
}
225+
}
226+
"ova" -> {
227+
// Filter catalog locally by type == OVA, ONA, Special, TV Special
228+
val ovaTypes = setOf("OVA", "ONA", "SPECIAL", "TV SPECIAL")
229+
val all = getCatalog().filter { it.type?.uppercase() in ovaTypes }
230+
val start = (page - 1) * pageSize
231+
val slice = if (start < all.size) all.subList(start, minOf(start + pageSize, all.size)) else emptyList()
232+
hasMore = (start + pageSize) < all.size
233+
slice.map { item ->
234+
newAnimeSearchResponse(item.title, item.slug, TvType.Anime) {
235+
this.posterUrl = if (item.cover.startsWith("/i/")) "$mainUrl${item.cover}" else item.cover
236+
}
237+
}
238+
}
206239
else -> emptyList()
207240
}
208241

@@ -232,14 +265,20 @@ class AnimeVerseProvider : MainAPI() {
232265
}
233266
}.reversed()
234267

268+
// The detail endpoint omits genres/studios/year — look them up from the catalog cache
269+
val catalogMeta = getCatalog().find { it.slug == slug }
270+
val genreTags = catalogMeta?.genres ?: emptyList()
271+
val studioTags = catalogMeta?.studios?.map { "Studio: $it" } ?: emptyList()
272+
val yearTag = catalogMeta?.year?.let { listOf("Year: $it") } ?: emptyList()
273+
val typeTag = catalogMeta?.type?.let { listOf(it) } ?: emptyList()
274+
val allTags = (typeTag + genreTags + studioTags + yearTag).distinct()
275+
235276
return newAnimeLoadResponse(details.title, url, tvType) {
236-
this.posterUrl = if (details.cover.startsWith("/i/")) "$mainUrl${details.cover}" else details.cover
237-
this.backgroundPosterUrl = details.thumb?.let {
238-
if (it.startsWith("/i/")) "$mainUrl$it" else it
239-
}
240-
this.plot = details.synopsis
241-
this.tags = details.genres
242-
this.episodes = mutableMapOf(dubStatus to episodes)
277+
this.posterUrl = if (details.cover.startsWith("/i/")) "$mainUrl${details.cover}" else details.cover
278+
this.backgroundPosterUrl = details.thumb?.let { if (it.startsWith("/i/")) "$mainUrl$it" else it }
279+
this.plot = details.synopsis
280+
this.tags = allTags.ifEmpty { null }
281+
this.episodes = mutableMapOf(dubStatus to episodes)
243282
}
244283
}
245284

0 commit comments

Comments
 (0)