feat: 添加 TvcatHandler,支持 /_fetch_p/ API 获取播放链接
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
package com.videoapp.tv.engine.tvcat
|
||||||
|
|
||||||
|
import com.videoapp.tv.data.SiteConfig
|
||||||
|
import com.videoapp.tv.engine.BaseSourceHandler
|
||||||
|
import com.videoapp.tv.engine.Episode
|
||||||
|
import com.videoapp.tv.engine.PlaySource
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class TvcatHandler : BaseSourceHandler(
|
||||||
|
id = "tvcat",
|
||||||
|
displayName = "tvcat (电视猫)",
|
||||||
|
baseUrl = "https://tvcat.cc",
|
||||||
|
config = SiteConfig(
|
||||||
|
baseUrl = "https://tvcat.cc",
|
||||||
|
searchPath = "/search",
|
||||||
|
searchMethod = "GET",
|
||||||
|
keywordParam = "q",
|
||||||
|
resultSelector = "li.col-md-2.col-sm-3.col-4",
|
||||||
|
titleSelector = "a[title]",
|
||||||
|
coverSelector = "img",
|
||||||
|
linkSelector = "a",
|
||||||
|
categorySelector = ".text-muted",
|
||||||
|
dateSelector = "",
|
||||||
|
episodeSelector = "li.list-inline-item a",
|
||||||
|
sourceSelector = "",
|
||||||
|
sourceEpisodeGroupSelector = "",
|
||||||
|
iframeSelector = "iframe",
|
||||||
|
videoSelector = "video source, video[src]"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override suspend fun extractVideos(detailUrl: String): List<PlaySource> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val doc = Jsoup.connect(detailUrl).timeout(15000).get()
|
||||||
|
val episodes = doc.select("li.list-inline-item a").mapNotNull { ep ->
|
||||||
|
val title = ep.text().trim()
|
||||||
|
val href = ep.attr("href").trim()
|
||||||
|
if (title.isNotEmpty() && href.isNotEmpty()) {
|
||||||
|
Episode(title, buildFullUrl(href))
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
if (episodes.isNotEmpty()) {
|
||||||
|
listOf(PlaySource("默认来源", episodes))
|
||||||
|
} else emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun resolvePlayUrl(playUrl: String): Pair<String?, String?> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val match = Regex("""(\d+)/ep(\d+)""").find(playUrl)
|
||||||
|
if (match != null) {
|
||||||
|
val videoId = match.groupValues[1]
|
||||||
|
val epNum = match.groupValues[2]
|
||||||
|
val apiUrl = "https://tvcat.cc/_fetch_p/$videoId/ep$epNum"
|
||||||
|
val conn = URL(apiUrl).openConnection()
|
||||||
|
conn.setRequestProperty("User-Agent", "Mozilla/5.0")
|
||||||
|
conn.setRequestProperty("Referer", playUrl)
|
||||||
|
conn.connectTimeout = 15000
|
||||||
|
conn.readTimeout = 15000
|
||||||
|
val json = conn.getInputStream().bufferedReader().use { it.readText() }
|
||||||
|
val obj = JSONObject(json)
|
||||||
|
val playcfgs = obj.getJSONArray("playcfgs")
|
||||||
|
if (playcfgs.length() > 0) {
|
||||||
|
val url = playcfgs.getJSONObject(0).getString("url")
|
||||||
|
Pair(url, null)
|
||||||
|
} else Pair(null, null)
|
||||||
|
} else Pair(null, null)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
Pair(null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user