1
+ package com.heyanle.easybangumi_extension.anfun
2
+
3
+ import android.util.Log
4
+ import com.heyanle.easybangumi4.source_api.ParserException
5
+ import com.heyanle.easybangumi4.source_api.SourceResult
6
+ import com.heyanle.easybangumi4.source_api.component.ComponentWrapper
7
+ import com.heyanle.easybangumi4.source_api.component.detailed.DetailedComponent
8
+ import com.heyanle.easybangumi4.source_api.component.play.PlayComponent
9
+ import com.heyanle.easybangumi4.source_api.component.update.UpdateComponent
10
+ import com.heyanle.easybangumi4.source_api.entity.Cartoon
11
+ import com.heyanle.easybangumi4.source_api.entity.CartoonImpl
12
+ import com.heyanle.easybangumi4.source_api.entity.CartoonSummary
13
+ import com.heyanle.easybangumi4.source_api.entity.Episode
14
+ import com.heyanle.easybangumi4.source_api.entity.PlayLine
15
+ import com.heyanle.easybangumi4.source_api.entity.PlayerInfo
16
+ import com.heyanle.easybangumi4.source_api.utils.api.OkhttpHelper
17
+ import com.heyanle.easybangumi4.source_api.utils.api.WebViewHelper
18
+ import com.heyanle.easybangumi4.source_api.utils.core.SourceUtils
19
+ import com.heyanle.easybangumi4.source_api.utils.core.network.GET
20
+ import com.heyanle.easybangumi4.source_api.withResult
21
+ import kotlinx.coroutines.Dispatchers
22
+ import kotlinx.coroutines.withTimeoutOrNull
23
+ import org.jsoup.Jsoup
24
+ import org.jsoup.nodes.Document
25
+
26
+ /* *
27
+ * Created by heyanle on 2024/1/29.
28
+ * https://github.com/heyanLE
29
+ */
30
+ class AnfunDetailedComponent (
31
+ private val okhttpHelper : OkhttpHelper ,
32
+ private val webViewHelper : WebViewHelper
33
+ ): ComponentWrapper(), DetailedComponent, UpdateComponent, PlayComponent {
34
+
35
+
36
+ override suspend fun getDetailed (summary : CartoonSummary ): SourceResult <Cartoon > {
37
+ return withResult(Dispatchers .IO ) {
38
+ detailed(getDoc(summary), summary)
39
+ }
40
+ }
41
+
42
+ override suspend fun getPlayLine (summary : CartoonSummary ): SourceResult <List <PlayLine >> {
43
+ return withResult(Dispatchers .IO ) {
44
+ playLine(getDoc(summary), summary)
45
+ }
46
+ }
47
+
48
+ override suspend fun getAll (summary : CartoonSummary ): SourceResult <Pair <Cartoon , List <PlayLine >>> {
49
+ return withResult(Dispatchers .IO ) {
50
+ detailed(getDoc(summary), summary) to playLine(getDoc(summary), summary)
51
+ }
52
+ }
53
+
54
+ override suspend fun update (
55
+ cartoon : Cartoon ,
56
+ oldPlayLine : List <PlayLine >
57
+ ): SourceResult <Cartoon > {
58
+ return withResult(Dispatchers .IO ) {
59
+
60
+ when (val n = getAll(CartoonSummary (cartoon.id, cartoon.source))) {
61
+ is SourceResult .Complete -> {
62
+ n.data.first.apply {
63
+
64
+ val newPlayLine = n.data.second
65
+
66
+ if (oldPlayLine.size != newPlayLine.size) {
67
+ isUpdate = true
68
+ } else {
69
+ isUpdate = false
70
+ for (i in oldPlayLine.indices) {
71
+ if (oldPlayLine[i].episode.size != newPlayLine[i].episode.size) {
72
+ isUpdate = true
73
+ break
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ is SourceResult .Error -> {
80
+ throw n.throwable
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ private fun getDoc (summary : CartoonSummary ): Document {
87
+ val d = okhttpHelper.cloudflareWebViewClient.newCall(GET (SourceUtils .urlParser(AnfunSource .ROOT_URL , " /anime/${summary.id} .html" )))
88
+ .execute().body?.string() ? : throw NullPointerException ()
89
+ return Jsoup .parse(d)
90
+ }
91
+ private fun playLine (document : Document , summary : CartoonSummary ): List <PlayLine > {
92
+ Log .e(" TAG" ," ------->>>>>>>playLine" )
93
+ val res = arrayListOf<PlayLine >()
94
+ val module = document.select(" .hl-play-source" ).first() ? : return res
95
+ val playNameList = module.select(" .hl-plays-wrap" ).first()?.select(" a" ) ? : return res
96
+ val playEpisodeList = module.select(" .hl-tabs-box" )
97
+ for (index in 0 .. playNameList.size) {
98
+ val playName = playNameList.getOrNull(index)?.text()
99
+ val playEpisode = playEpisodeList.getOrNull(index)
100
+ if (playName != null && playEpisode != null ) {
101
+ val results = playEpisode.select(" li" ).select(" a" )
102
+ val es = arrayListOf<Episode >()
103
+
104
+ for (i in results.indices) {
105
+ es.add(Episode ((i+ 1 ).toString(), results[i].text(), i)) // title
106
+
107
+ }
108
+ val playLine = PlayLine (
109
+ id = (index + 1 ).toString(),
110
+ label = playName,
111
+ episode = es
112
+ )
113
+ res.add(playLine)
114
+ }
115
+ }
116
+ return res
117
+ }
118
+
119
+ override suspend fun getPlayInfo (
120
+ summary : CartoonSummary ,
121
+ playLine : PlayLine ,
122
+ episode : Episode
123
+ ): SourceResult <PlayerInfo > {
124
+ Log .e(" TAG" ," ------->>>>>>>开始播放" )
125
+ return withResult(Dispatchers .IO ) {
126
+
127
+ // Log.e("TAG","${playUrlTemp[playLine.id.toInt()]}") // [/play/632-1-1.html]
128
+ val url = SourceUtils .urlParser(AnfunSource .ROOT_URL , " /play/${summary.id} -${playLine.id} -${episode.id} " )
129
+ // Log.e("TAG", url) // https://www.anfuns.cc/play/632-1-1.html
130
+ var videoUrl = webViewHelper.interceptResource(
131
+ url, regex = " https://www.anfuns.cc/vapi/AIRA/mui.php?.*"
132
+ )
133
+ Log .e(" TAG" , " 地址:$videoUrl " )
134
+ if (videoUrl.isNotEmpty()) {
135
+ when {
136
+ videoUrl.contains(" .m3u8&" ) -> videoUrl = videoUrl.substringAfter(" url=" )
137
+ .substringBefore(" &" )
138
+ videoUrl.contains(" .mp4" ) -> videoUrl = videoUrl.substringAfter(" url=" )
139
+ .substringBefore(" &next=" )
140
+ }
141
+ Log .e(" TAG" , " 解析后url:$videoUrl " )
142
+ if (videoUrl.indexOf(" .mp4" ) != - 1 ){
143
+ PlayerInfo (
144
+ decodeType = PlayerInfo .DECODE_TYPE_OTHER ,
145
+ uri = SourceUtils .urlParser(AnfunSource .ROOT_URL ,videoUrl)
146
+ )
147
+ }else {
148
+ PlayerInfo (
149
+ decodeType = PlayerInfo .DECODE_TYPE_HLS ,
150
+ uri = SourceUtils .urlParser(AnfunSource .ROOT_URL ,videoUrl)
151
+ )
152
+ }
153
+ }else {
154
+ throw ParserException (" Unknown" )
155
+ }
156
+ }
157
+ }
158
+
159
+ private fun detailed (document : Document , summary : CartoonSummary ): Cartoon {
160
+ Log .e(" TAG" ," ------->>>>>>>detailed" )
161
+
162
+ var desc = " "
163
+ var update = 0
164
+ var status = 0
165
+
166
+ val cover = document.select(" .hl-dc-pic" ).select(" span" ).attr(" data-original" )
167
+ val title = document.select(" .hl-dc-headwrap" ).select(" .hl-dc-title" ).text()
168
+ // document.select(".hl-dc-headwrap").select(".hl-dc-sub").text()
169
+ // 更新状况
170
+ val upStateItems = document.select(" .hl-dc-content" )
171
+ .select(" .hl-vod-data" ).select(" .hl-full-box" ).select(" ul" ).select(" li" )
172
+ for (upStateEm in upStateItems){
173
+ val t = upStateEm.text()
174
+ when {
175
+ t.contains(" 状态:" ) -> {
176
+ status =
177
+ if (t.startsWith(" 连载" )) Cartoon .STATUS_ONGOING
178
+ else if (t.startsWith(" 全" )) Cartoon .STATUS_COMPLETED
179
+ else Cartoon .STATUS_UNKNOWN
180
+ val isTheater = title.contains(" 剧场版" )
181
+ update =
182
+ if (isTheater) {
183
+ if (status == Cartoon .STATUS_COMPLETED ) {
184
+ Cartoon .UPDATE_STRATEGY_NEVER
185
+ } else {
186
+ Cartoon .UPDATE_STRATEGY_ONLY_STRICT
187
+ }
188
+ } else {
189
+ if (status == Cartoon .STATUS_COMPLETED ) {
190
+ Cartoon .UPDATE_STRATEGY_ONLY_STRICT
191
+ } else {
192
+ Cartoon .UPDATE_STRATEGY_ALWAYS
193
+ }
194
+ }
195
+ }
196
+ t.contains(" 简介:" ) -> desc = t
197
+ }
198
+ }
199
+
200
+ return CartoonImpl (
201
+ id = summary.id,
202
+ url = SourceUtils .urlParser(AnfunSource .ROOT_URL , " /anime/${summary.id} .html" ),
203
+ source = summary.source,
204
+
205
+ title = title,
206
+ coverUrl = cover,
207
+
208
+ intro = " " ,
209
+ description = desc,
210
+
211
+ genre = " " ,
212
+
213
+ status = status,
214
+ updateStrategy = update,
215
+ )
216
+ }
217
+
218
+
219
+ }
0 commit comments