Skip to content

Commit 70f1fe2

Browse files
Last Sync: 2026-02-24 05:44 (Mobile)
1 parent 3da25ef commit 70f1fe2

1 file changed

Lines changed: 207 additions & 60 deletions

File tree

MovieboxProvider/src/main/kotlin/com/moviebox/MovieboxProvider.kt

Lines changed: 207 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,25 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
1111
import okhttp3.RequestBody.Companion.toRequestBody
1212

1313
class MovieboxProvider : MainAPI() {
14-
1514
override var mainUrl = "https://moviebox.ph"
16-
private val apiUrl = "https://fmoviesunblocked.net"
15+
private val mainAPIUrl = "https://h5-api.aoneroom.com"
16+
private val secondAPIUrl = "https://filmboom.top"
1717
override val instantLinkLoading = true
1818
override var name = "Moviebox"
1919
override val hasMainPage = true
2020
override val hasQuickSearch = true
2121
override var lang = "id"
22-
override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries, TvType.Anime, TvType.AsianDrama)
22+
override val supportedTypes = setOf(
23+
TvType.Movie,
24+
TvType.TvSeries,
25+
TvType.Anime,
26+
TvType.AsianDrama
27+
)
2328

24-
override val mainPage = mainPageOf(
29+
override val mainPage: List<MainPageData> = mainPageOf(
2530
"872031290915189720" to "Trending Now",
2631
"997144265920760504" to "Popular Movie",
27-
"5283462032510044280" to "Drama Indonesia Terkini",
32+
"5283462032510044280" to "Latest Indonesian Drama",
2833
"6528093688173053896" to "Trending Indonesian Movies",
2934
"4380734070238626200" to "K-Drama",
3035
"7736026911486755336" to "Western TV",
@@ -33,120 +38,247 @@ class MovieboxProvider : MainAPI() {
3338
"5848753831881965888" to "Indonesian Horror Stories",
3439
"1164329479448281992" to "Thai-Drama",
3540
"7132534597631837112" to "Animated Film",
41+
"1,ForYou" to "Movie ForYou",
42+
"1,Hottest" to "Movie Hottest",
43+
"1,Latest" to "Movie Latest",
44+
"1,Rating" to "Movie Rating",
45+
"2,ForYou" to "TVShow ForYou",
46+
"2,Hottest" to "TVShow Hottest",
47+
"2,Latest" to "TVShow Latest",
48+
"2,Rating" to "TVShow Rating",
49+
"1006,ForYou" to "Animation ForYou",
50+
"1006,Hottest" to "Animation Hottest",
51+
"1006,Latest" to "Animation Latest",
52+
"1006,Rating" to "Animation Rating",
3653
)
3754

38-
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
39-
val url = "$mainUrl/wefeed-h5-bff/web/ranking-list/content?id=${request.data}&page=$page&perPage=12"
40-
val home = app.get(url).parsedSafe<Media>()?.data?.subjectList?.map { it.toSearchResponse(this) }
41-
?: throw ErrorLoadingException("No Data Found")
55+
override suspend fun getMainPage(
56+
page: Int,
57+
request: MainPageRequest,
58+
): HomePageResponse {
59+
60+
val home = mutableListOf<SearchResponse>()
61+
62+
if(!request.data.contains(",")) {
63+
val url = "$mainAPIUrl/wefeed-h5api-bff/ranking-list/content?id=${request.data}&page=$page&perPage=12"
64+
65+
val index = app.get(url).parsedSafe<Media>()?.data?.subjectList?.map {
66+
it.toSearchResponse(this)
67+
} ?: throw ErrorLoadingException("No Data Found")
68+
69+
home.addAll(index)
70+
} else {
71+
val params = request.data.split(",")
72+
val body = mapOf(
73+
"channelId" to params.first(),
74+
"page" to page,
75+
"perPage" to "28",
76+
"sort" to params.last()
77+
).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
78+
79+
val index = app.post("$mainAPIUrl/wefeed-h5api-bff/subject/filter", requestBody = body)
80+
.parsedSafe<Media>()?.data?.items?.map {
81+
it.toSearchResponse(this)
82+
} ?: throw ErrorLoadingException("No Data Found")
83+
84+
home.addAll(index)
85+
}
86+
87+
4288
return newHomePageResponse(request.name, home)
4389
}
4490

4591
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
4692

4793
override suspend fun search(query: String): List<SearchResponse> {
48-
val body = mapOf("keyword" to query, "page" to "1", "perPage" to "0", "subjectType" to "0")
49-
.toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
50-
return app.post("$mainUrl/wefeed-h5-bff/web/subject/search", requestBody = body)
51-
.parsedSafe<Media>()?.data?.items?.map { it.toSearchResponse(this) } ?: throw ErrorLoadingException()
94+
return app.post(
95+
"$secondAPIUrl/wefeed-h5-bff/web/subject/search", requestBody = mapOf(
96+
"keyword" to query,
97+
"page" to "1",
98+
"perPage" to "0",
99+
"subjectType" to "0",
100+
).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
101+
).parsedSafe<Media>()?.data?.items?.map { it.toSearchResponse(this) }
102+
?: throw ErrorLoadingException()
52103
}
53104

54105
override suspend fun load(url: String): LoadResponse {
55106
val id = url.substringAfterLast("/")
56-
val doc = app.get("$mainUrl/wefeed-h5-bff/web/subject/detail?subjectId=$id").parsedSafe<MediaDetail>()?.data
57-
val subject = doc?.subject
107+
val document = app.get("$secondAPIUrl/wefeed-h5-bff/web/subject/detail?subjectId=$id")
108+
.parsedSafe<MediaDetail>()?.data
109+
val subject = document?.subject
58110
val title = subject?.title ?: ""
59111
val poster = subject?.cover?.url
60112
val tags = subject?.genre?.split(",")?.map { it.trim() }
113+
61114
val year = subject?.releaseDate?.substringBefore("-")?.toIntOrNull()
62115
val tvType = if (subject?.subjectType == 2) TvType.TvSeries else TvType.Movie
63116
val description = subject?.description
64117
val trailer = subject?.trailer?.videoAddress?.url
65-
val mediaScore = subject?.imdbRatingValue?.toDoubleOrNull()?.let { Score.from10(it) }
66-
val actors = doc?.stars?.mapNotNull { cast ->
67-
ActorData(Actor(cast.name ?: return@mapNotNull null, cast.avatarUrl), roleString = cast.character)
118+
val rating = subject?.imdbRatingValue?.toIntOrNull()
119+
val actors = document?.stars?.mapNotNull { cast ->
120+
ActorData(
121+
Actor(
122+
cast.name ?: return@mapNotNull null,
123+
cast.avatarUrl
124+
),
125+
roleString = cast.character
126+
)
68127
}?.distinctBy { it.actor }
69-
val recommendations = app.get("$mainUrl/wefeed-h5-bff/web/subject/detail-rec?subjectId=$id&page=1&perPage=12")
70-
.parsedSafe<Media>()?.data?.items?.map { it.toSearchResponse(this) }
128+
129+
val recommendations =
130+
app.get("$mainUrl/wefeed-h5-bff/web/subject/detail-rec?subjectId=$id&page=1&perPage=12")
131+
.parsedSafe<Media>()?.data?.items?.map {
132+
it.toSearchResponse(this)
133+
}
71134

72135
return if (tvType == TvType.TvSeries) {
73-
val episodes = doc?.resource?.seasons?.map { seasons ->
74-
(if (seasons.allEp.isNullOrEmpty()) (1..seasons.maxEp!!) else seasons.allEp.split(",").map { it.toInt() })
75-
.map { episode -> newEpisode(LoadData(id, seasons.se, episode, subject?.detailPath).toJson()) {
76-
this.season = seasons.se
77-
this.episode = episode
78-
}}
136+
val episode = document?.resource?.seasons?.map { seasons ->
137+
(if (seasons.allEp.isNullOrEmpty()) (1..seasons.maxEp!!) else seasons.allEp.split(",")
138+
.map { it.toInt() })
139+
.map { episode ->
140+
newEpisode(
141+
LoadData(
142+
id,
143+
seasons.se,
144+
episode,
145+
subject?.detailPath
146+
).toJson()
147+
) {
148+
this.season = seasons.se
149+
this.episode = episode
150+
}
151+
}
79152
}?.flatten() ?: emptyList()
80-
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
153+
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episode) {
81154
this.posterUrl = poster
82155
this.year = year
83156
this.plot = description
84157
this.tags = tags
85-
this.score = mediaScore
158+
this.score = Score.from10(rating)
86159
this.actors = actors
87160
this.recommendations = recommendations
88161
addTrailer(trailer, addRaw = true)
89162
}
90163
} else {
91-
newMovieLoadResponse(title, url, TvType.Movie, LoadData(id, detailPath = subject?.detailPath).toJson()) {
164+
newMovieLoadResponse(
165+
title,
166+
url,
167+
TvType.Movie,
168+
LoadData(id, detailPath = subject?.detailPath).toJson()
169+
) {
92170
this.posterUrl = poster
93171
this.year = year
94172
this.plot = description
95173
this.tags = tags
96-
this.score = mediaScore
174+
this.score = Score.from10(rating)
97175
this.actors = actors
98176
this.recommendations = recommendations
99177
addTrailer(trailer, addRaw = true)
100178
}
101179
}
102180
}
103181

104-
override suspend fun loadLinks(data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean {
182+
override suspend fun loadLinks(
183+
data: String,
184+
isCasting: Boolean,
185+
subtitleCallback: (SubtitleFile) -> Unit,
186+
callback: (ExtractorLink) -> Unit
187+
): Boolean {
188+
105189
val media = parseJson<LoadData>(data)
106-
val referer = "$apiUrl/spa/videoPlayPage/movies/${media.detailPath}?id=${media.id}&type=/movie/detail&lang=en"
107-
val streams = app.get("$apiUrl/wefeed-h5-bff/web/subject/play?subjectId=${media.id}&se=${media.season ?: 0}&ep=${media.episode ?: 0}", referer = referer)
108-
.parsedSafe<Media>()?.data?.streams
109-
110-
streams?.reversed()?.distinctBy { it.url }?.forEach { source ->
111-
callback(newExtractorLink(this.name, this.name, source.url ?: return@forEach, INFER_TYPE) {
112-
this.referer = "$apiUrl/"
113-
this.quality = getQualityFromName(source.resolutions)
114-
})
190+
val referer = "$secondAPIUrl/spa/videoPlayPage/movies/${media.detailPath}?id=${media.id}&type=/movie/detail&lang=en"
191+
192+
val streams = app.get(
193+
"$secondAPIUrl/wefeed-h5-bff/web/subject/play?subjectId=${media.id}&se=${media.season ?: 0}&ep=${media.episode ?: 0}",
194+
referer = referer
195+
).parsedSafe<Media>()?.data?.streams
196+
197+
streams?.reversed()?.distinctBy { it.url }?.map { source ->
198+
callback.invoke(
199+
newExtractorLink(
200+
this.name,
201+
this.name,
202+
source.url ?: return@map,
203+
INFER_TYPE
204+
) {
205+
this.referer = "$secondAPIUrl/"
206+
this.quality = getQualityFromName(source.resolutions)
207+
}
208+
)
115209
}
116210

117211
val id = streams?.first()?.id
118212
val format = streams?.first()?.format
119-
app.get("$apiUrl/wefeed-h5-bff/web/subject/caption?format=$format&id=$id&subjectId=${media.id}", referer = referer)
120-
.parsedSafe<Media>()?.data?.captions?.forEach { subtitle ->
121-
subtitleCallback(SubtitleFile(subtitle.lanName ?: "", subtitle.url ?: return@forEach))
122-
}
213+
214+
app.get(
215+
"$secondAPIUrl/wefeed-h5-bff/web/subject/caption?format=$format&id=$id&subjectId=${media.id}",
216+
referer = referer
217+
).parsedSafe<Media>()?.data?.captions?.map { subtitle ->
218+
subtitleCallback.invoke(
219+
newSubtitleFile(
220+
subtitle.lanName ?: "",
221+
subtitle.url ?: return@map
222+
)
223+
)
224+
}
123225

124226
return true
125227
}
126228

127-
data class LoadData(val id: String? = null, val season: Int? = null, val episode: Int? = null, val detailPath: String? = null)
229+
data class LoadData(
230+
val id: String? = null,
231+
val season: Int? = null,
232+
val episode: Int? = null,
233+
val detailPath: String? = null,
234+
)
128235

129-
data class Media(@JsonProperty("data") val data: Data? = null) {
236+
data class Media(
237+
@JsonProperty("data") val data: Data? = null,
238+
) {
130239
data class Data(
131240
@JsonProperty("subjectList") val subjectList: ArrayList<Items>? = arrayListOf(),
132241
@JsonProperty("items") val items: ArrayList<Items>? = arrayListOf(),
133242
@JsonProperty("streams") val streams: ArrayList<Streams>? = arrayListOf(),
134-
@JsonProperty("captions") val captions: ArrayList<Captions>? = arrayListOf()
243+
@JsonProperty("captions") val captions: ArrayList<Captions>? = arrayListOf(),
135244
) {
136-
data class Streams(@JsonProperty("id") val id: String? = null, @JsonProperty("format") val format: String? = null, @JsonProperty("url") val url: String? = null, @JsonProperty("resolutions") val resolutions: String? = null)
137-
data class Captions(@JsonProperty("lan") val lan: String? = null, @JsonProperty("lanName") val lanName: String? = null, @JsonProperty("url") val url: String? = null)
245+
data class Streams(
246+
@JsonProperty("id") val id: String? = null,
247+
@JsonProperty("format") val format: String? = null,
248+
@JsonProperty("url") val url: String? = null,
249+
@JsonProperty("resolutions") val resolutions: String? = null,
250+
)
251+
252+
data class Captions(
253+
@JsonProperty("lan") val lan: String? = null,
254+
@JsonProperty("lanName") val lanName: String? = null,
255+
@JsonProperty("url") val url: String? = null,
256+
)
138257
}
139258
}
140259

141-
data class MediaDetail(@JsonProperty("data") val data: Data? = null) {
260+
data class MediaDetail(
261+
@JsonProperty("data") val data: Data? = null,
262+
) {
142263
data class Data(
143264
@JsonProperty("subject") val subject: Items? = null,
144265
@JsonProperty("stars") val stars: ArrayList<Stars>? = arrayListOf(),
145-
@JsonProperty("resource") val resource: Resource? = null
266+
@JsonProperty("resource") val resource: Resource? = null,
146267
) {
147-
data class Stars(@JsonProperty("name") val name: String? = null, @JsonProperty("character") val character: String? = null, @JsonProperty("avatarUrl") val avatarUrl: String? = null)
148-
data class Resource(@JsonProperty("seasons") val seasons: ArrayList<Seasons>? = arrayListOf()) {
149-
data class Seasons(@JsonProperty("se") val se: Int? = null, @JsonProperty("maxEp") val maxEp: Int? = null, @JsonProperty("allEp") val allEp: String? = null)
268+
data class Stars(
269+
@JsonProperty("name") val name: String? = null,
270+
@JsonProperty("character") val character: String? = null,
271+
@JsonProperty("avatarUrl") val avatarUrl: String? = null,
272+
)
273+
274+
data class Resource(
275+
@JsonProperty("seasons") val seasons: ArrayList<Seasons>? = arrayListOf(),
276+
) {
277+
data class Seasons(
278+
@JsonProperty("se") val se: Int? = null,
279+
@JsonProperty("maxEp") val maxEp: Int? = null,
280+
@JsonProperty("allEp") val allEp: String? = null,
281+
)
150282
}
151283
}
152284
}
@@ -163,16 +295,31 @@ class MovieboxProvider : MainAPI() {
163295
@JsonProperty("imdbRatingValue") val imdbRatingValue: String? = null,
164296
@JsonProperty("countryName") val countryName: String? = null,
165297
@JsonProperty("trailer") val trailer: Trailer? = null,
166-
@JsonProperty("detailPath") val detailPath: String? = null
298+
@JsonProperty("detailPath") val detailPath: String? = null,
167299
) {
300+
168301
fun toSearchResponse(provider: MovieboxProvider): SearchResponse {
169-
return provider.newMovieSearchResponse(title ?: "", subjectId!!, if (subjectType == 1) TvType.Movie else TvType.TvSeries, false) {
302+
return provider.newMovieSearchResponse(
303+
title ?: "",
304+
subjectId ?: "",
305+
if (subjectType == 1) TvType.Movie else TvType.TvSeries,
306+
false
307+
) {
170308
this.posterUrl = cover?.url
171309
}
172310
}
173-
data class Cover(@JsonProperty("url") val url: String? = null)
174-
data class Trailer(@JsonProperty("videoAddress") val videoAddress: VideoAddress? = null) {
175-
data class VideoAddress(@JsonProperty("url") val url: String? = null)
311+
312+
data class Cover(
313+
@JsonProperty("url") val url: String? = null,
314+
)
315+
316+
data class Trailer(
317+
@JsonProperty("videoAddress") val videoAddress: VideoAddress? = null,
318+
) {
319+
data class VideoAddress(
320+
@JsonProperty("url") val url: String? = null,
321+
)
176322
}
177323
}
178-
}
324+
325+
}

0 commit comments

Comments
 (0)