my-tv/app/src/main/java/com/lizongying/mytv/Request.kt

347 lines
14 KiB
Kotlin
Raw Normal View History

2023-12-15 13:04:32 +08:00
package com.lizongying.mytv
import android.content.Context
2023-12-23 23:34:53 +08:00
import android.os.Handler
import android.os.Looper
2023-12-15 18:12:11 +08:00
import android.util.Base64
2023-12-15 13:04:32 +08:00
import android.util.Log
2023-12-28 20:30:12 +08:00
import com.lizongying.mytv.Utils.getDateFormat
2023-12-15 13:04:32 +08:00
import com.lizongying.mytv.api.ApiClient
2023-12-23 23:34:53 +08:00
import com.lizongying.mytv.api.BtraceClient
2024-01-05 14:53:54 +08:00
import com.lizongying.mytv.api.Info
2023-12-15 13:04:32 +08:00
import com.lizongying.mytv.api.LiveInfo
import com.lizongying.mytv.api.LiveInfoRequest
import com.lizongying.mytv.api.ProtoClient
import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.api.YSPApiService
2023-12-23 23:34:53 +08:00
import com.lizongying.mytv.api.YSPBtraceService
2023-12-15 13:04:32 +08:00
import com.lizongying.mytv.api.YSPProtoService
2024-01-05 14:53:54 +08:00
import com.lizongying.mytv.api.YSPTokenService
2023-12-15 13:04:32 +08:00
import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
2023-12-21 17:51:26 +08:00
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
2023-12-15 13:04:32 +08:00
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
2023-12-15 18:12:11 +08:00
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
2023-12-15 13:04:32 +08:00
2024-01-05 14:53:54 +08:00
class Request {
private var yspTokenService: YSPTokenService = ApiClient().yspTokenService
private var yspApiService: YSPApiService = ApiClient().yspApiService
private var yspBtraceService: YSPBtraceService = BtraceClient().yspBtraceService
private var yspProtoService: YSPProtoService = ProtoClient().yspProtoService
2023-12-15 13:04:32 +08:00
private var ysp: YSP? = null
2023-12-28 20:30:12 +08:00
// TODO onDestroy
2023-12-23 23:34:53 +08:00
private val handler = Handler(Looper.getMainLooper())
private lateinit var myRunnable: MyRunnable
2023-12-15 13:04:32 +08:00
private var mapping = mapOf(
2023-12-22 00:02:57 +08:00
"CCTV4K" to "CCTV4K 超高清",
2023-12-15 13:04:32 +08:00
"CCTV1" to "CCTV1 综合",
"CCTV2" to "CCTV2 财经",
"CCTV4" to "CCTV4 中文国际",
"CCTV5" to "CCTV5 体育",
"CCTV5+" to "CCTV5+ 体育赛事",
"CCTV7" to "CCTV7 国防军事",
"CCTV9" to "CCTV9 记录",
"CCTV10" to "CCTV10 科教",
"CCTV11" to "CCTV11 戏曲",
"CCTV12" to "CCTV12 社会与法",
2023-12-18 10:12:52 +08:00
"CCTV13" to "CCTV13 新闻",
2023-12-15 13:04:32 +08:00
"CCTV14" to "CCTV14 少儿",
"CCTV15" to "CCTV15 音乐",
"CCTV16-HD" to "CCTV16 奥林匹克",
"CCTV17" to "CCTV17 农业农村",
"CGTN" to "CGTN",
"CGTN法语频道" to "CGTN 法语频道",
"CGTN俄语频道" to "CGTN 俄语频道",
"CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道",
"CGTN西班牙语频道" to "CGTN 西班牙语频道",
2023-12-24 12:00:16 +08:00
"CGTN外语纪录频道" to "CGTN 纪录频道",
2023-12-15 13:04:32 +08:00
"东方卫视" to "东方卫视",
"湖南卫视" to "湖南卫视",
"湖北卫视" to "湖北卫视",
"辽宁卫视" to "辽宁卫视",
"江苏卫视" to "江苏卫视",
"江西卫视" to "江西卫视",
"山东卫视" to "山东卫视",
"广东卫视" to "广东卫视",
"广西卫视" to "广西卫视",
"重庆卫视" to "重庆卫视",
"河南卫视" to "河南卫视",
"河北卫视" to "河北卫视",
"贵州卫视" to "贵州卫视",
"北京卫视" to "北京卫视",
"黑龙江卫视" to "黑龙江卫视",
"浙江卫视" to "浙江卫视",
"安徽卫视" to "安徽卫视",
"深圳卫视" to "深圳卫视",
"四川卫视" to "四川卫视",
"福建东南卫视" to "东南卫视",
"海南卫视" to "海南卫视",
)
2024-01-05 14:53:54 +08:00
private var token: String? = null
fun initYSP(context: Context) {
ysp = YSP(context)
}
2024-01-05 18:24:44 +08:00
fun fetchVideo(tvModel: TVViewModel, cookie: String) {
2023-12-23 23:34:53 +08:00
if (::myRunnable.isInitialized) {
handler.removeCallbacks(myRunnable)
}
2023-12-20 14:55:39 +08:00
val title = tvModel.title.value
2023-12-15 13:04:32 +08:00
2024-01-05 18:24:44 +08:00
tvModel.seq = 0
val data = ysp?.switch(tvModel)
2023-12-15 13:04:32 +08:00
val request = data?.let { LiveInfoRequest(it) }
2024-01-05 14:53:54 +08:00
request?.let { yspApiService.getLiveInfo(cookie, it) }
2023-12-15 13:04:32 +08:00
?.enqueue(object : Callback<LiveInfo> {
override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) {
if (response.isSuccessful) {
val liveInfo = response.body()
if (liveInfo?.data?.playurl != null) {
2023-12-15 18:12:11 +08:00
val chanll = liveInfo.data.chanll
val decodedBytes = Base64.decode(
chanll.substring(9, chanll.length - 3),
Base64.DEFAULT
)
val decodedString = String(decodedBytes)
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val matchResult = regex.find(decodedString)
if (matchResult != null) {
val (key, iv) = matchResult.destructured
2023-12-18 10:12:52 +08:00
val keyBytes = Base64.decode(key, Base64.DEFAULT)
val ivBytes = Base64.decode(iv, Base64.DEFAULT)
val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
2023-12-16 08:19:02 +08:00
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
2023-12-15 18:12:11 +08:00
ivBytes
2023-12-16 08:19:02 +08:00
).uppercase()
2023-12-18 10:12:52 +08:00
Log.i(TAG, "$title url $url")
tvModel.addVideoUrl(url)
2023-12-20 14:55:39 +08:00
tvModel.allReady()
2024-01-08 20:07:53 +08:00
tvModel.retryTimes = 0
2023-12-23 23:34:53 +08:00
myRunnable = MyRunnable(tvModel)
handler.post(myRunnable)
2023-12-18 10:12:52 +08:00
} else {
Log.e(TAG, "$title key error")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2023-12-18 10:12:52 +08:00
}
} else {
2023-12-18 12:47:21 +08:00
Log.e(TAG, "$title url error $request")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2023-12-15 13:04:32 +08:00
}
2023-12-18 10:12:52 +08:00
} else {
Log.e(TAG, "$title status error")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2023-12-15 13:04:32 +08:00
}
}
override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
2023-12-20 14:55:39 +08:00
Log.e(TAG, "$title request error")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2023-12-15 13:04:32 +08:00
}
})
}
2024-01-05 18:24:44 +08:00
fun fetchData(tvModel: TVViewModel) {
var cookie = "guid=1; vplatform=109"
val channels = arrayOf(
"CCTV3 综艺",
"CCTV6 电影",
"CCTV8 电视剧",
"风云剧场",
"第一剧场",
"怀旧剧场",
"世界地理",
"风云音乐",
"兵器科技",
"风云足球",
"高尔夫网球",
"女性时尚",
"央视文化精品",
"央视台球",
"电视指南",
"卫生健康",
)
if (tvModel.title.value in channels) {
yspTokenService.getInfo()
.enqueue(object : Callback<Info> {
override fun onResponse(call: Call<Info>, response: Response<Info>) {
if (response.isSuccessful) {
val info = response.body()
token = info?.data?.token
Log.i(TAG, "info success $token")
cookie =
"guid=1; vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
fetchVideo(tvModel, cookie)
} else {
Log.e(TAG, "info status error")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2024-01-05 18:24:44 +08:00
}
}
override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "info request error $t")
2024-01-08 20:07:53 +08:00
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchData(tvModel)
}
2024-01-05 18:24:44 +08:00
}
})
} else {
fetchVideo(tvModel, cookie)
}
}
2023-12-23 23:34:53 +08:00
inner class MyRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() {
fetchBtrace(tvModel)
handler.postDelayed(this, 60000)
}
}
fun fetchBtrace(tvModel: TVViewModel) {
val title = tvModel.title.value
val guid = ysp?.getGuid()!!
val pid = tvModel.pid.value!!
val sid = tvModel.sid.value!!
2024-01-05 14:53:54 +08:00
yspBtraceService.kvcollect(
2023-12-23 23:34:53 +08:00
c_timestamp = ysp?.generateGuid()!!,
guid = guid,
c_guid = guid,
prog = sid,
viewid = sid,
fpid = pid,
livepid = pid,
sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid",
playno = ysp?.getRand()!!,
2023-12-28 20:30:12 +08:00
ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"),
2023-12-23 23:34:53 +08:00
seq = tvModel.seq.toString(),
)
2024-01-05 14:53:54 +08:00
.enqueue(object : Callback<Void> {
2023-12-23 23:34:53 +08:00
override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) {
2024-01-05 14:53:54 +08:00
// Log.d(TAG, "$title kvcollect success")
2023-12-23 23:34:53 +08:00
} else {
2023-12-24 12:00:16 +08:00
Log.e(TAG, "$title kvcollect status error")
2023-12-23 23:34:53 +08:00
}
}
override fun onFailure(call: Call<Void>, t: Throwable) {
2023-12-24 12:00:16 +08:00
Log.e(TAG, "$title kvcollect request error")
2023-12-23 23:34:53 +08:00
}
})
tvModel.seq++
}
2023-12-15 13:04:32 +08:00
fun fetchPage() {
2024-01-05 14:53:54 +08:00
yspProtoService.getPage().enqueue(object : Callback<pageModel.Response> {
2023-12-15 13:04:32 +08:00
override fun onResponse(
call: Call<pageModel.Response>,
response: Response<pageModel.Response>
) {
if (response.isSuccessful) {
val body = response.body()
if (body?.data?.feedModuleListCount == 1) {
for (item in body.data?.feedModuleListList!![0]?.dataTvChannelListList!!) {
if (item.isVip && !item.isLimitedFree) {
continue
}
Log.i(
TAG,
2024-01-07 21:36:08 +08:00
"${item.channelName},${item.pid},${item.streamId}"
2023-12-15 13:04:32 +08:00
)
var channelType = "央视频道"
if (item?.channelType === "weishi") {
channelType = "地方频道"
}
if (!mapping.containsKey(item.channelName)) {
continue
}
2023-12-20 14:55:39 +08:00
val tv =
TVList.list[channelType]?.find { it.title == mapping[item.channelName] }
2023-12-15 13:04:32 +08:00
if (tv != null) {
tv.logo = item.tvLogo
tv.pid = item.pid
tv.sid = item.streamId
}
}
}
}
}
override fun onFailure(call: Call<pageModel.Response>, t: Throwable) {
Log.e(TAG, "Page request failed", t)
}
})
}
2023-12-28 20:30:12 +08:00
fun fetchProgram(tvViewModel: TVViewModel) {
val title = tvViewModel.title.value
2024-01-05 14:53:54 +08:00
yspProtoService.getProgram(tvViewModel.programId.value!!, getDateFormat("yyyyMMdd"))
.enqueue(object : Callback<epgProgramModel.Response> {
2023-12-21 17:51:26 +08:00
override fun onResponse(
call: Call<epgProgramModel.Response>,
response: Response<epgProgramModel.Response>
) {
if (response.isSuccessful) {
val program = response.body()
if (program != null) {
2023-12-28 20:30:12 +08:00
tvViewModel.addProgram(program.dataListList)
Log.i(TAG, "$title program ${program.dataListList.size}")
2023-12-21 17:51:26 +08:00
}
}
2023-12-15 13:04:32 +08:00
}
2023-12-21 17:51:26 +08:00
override fun onFailure(call: Call<epgProgramModel.Response>, t: Throwable) {
2023-12-28 20:30:12 +08:00
Log.e(TAG, "$title program request failed $t")
2023-12-21 17:51:26 +08:00
}
})
2023-12-15 13:04:32 +08:00
}
2023-12-15 18:12:11 +08:00
private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String {
2023-12-20 14:55:39 +08:00
val plaintext =
2023-12-15 18:12:11 +08:00
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,AppleM1Pro,OpenGL4.1)"}"""
return try {
val keySpec = SecretKeySpec(key, "DESede")
val ivSpec = IvParameterSpec(iv)
2023-12-16 08:19:02 +08:00
val cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding")
2023-12-15 18:12:11 +08:00
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encryptedBytes = cipher.doFinal(plaintext.toByteArray())
2023-12-16 08:19:02 +08:00
return encryptedBytes.let { it -> it.joinToString("") { "%02x".format(it) } }
2023-12-15 18:12:11 +08:00
} catch (e: Exception) {
e.printStackTrace()
""
}
}
2023-12-15 13:04:32 +08:00
companion object {
private const val TAG = "Request"
}
}