初始提交:第三方合作伙伴 App 测试项目
- 支持 EPG Intent 传参启动(deviceUid + programCode 自动播放) - 代销模式 init/checkBenefits/SDK初始化/获取播放地址/播放 - Android 4.2 (API 17) 兼容 - OkHttp 3.12.x + TLS 1.2 - ExoPlayer 2.19.1 视频播放
This commit is contained in:
269
app/src/main/java/com/pukka/mediaapp/sdk/JetsenxSdkUtil.kt
Normal file
269
app/src/main/java/com/pukka/mediaapp/sdk/JetsenxSdkUtil.kt
Normal file
@@ -0,0 +1,269 @@
|
||||
package com.pukka.mediaapp.sdk
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.jetsenxsdk.api.JetsenxSdk
|
||||
import com.jetsenxsdk.bean.VideoInfo
|
||||
import com.jetsenxsdk.callback.JetsenxInitCallback
|
||||
import com.jetsenxsdk.callback.JetsenxPricesCallback
|
||||
import com.jetsenxsdk.callback.JetsenxUrlCallback
|
||||
import com.pukka.mediaapp.utils.FileLogger
|
||||
|
||||
/**
|
||||
* JetsenxSdk Kotlin 封装
|
||||
*
|
||||
* 视频地址获取流程:
|
||||
* 1. init() 初始化SDK(用 thirdCardNo 作为 uid)
|
||||
* 2. getAllVideoUrl() -> getAllAssetPrices(获取productCode) -> getVideoUrls/getFreeVideoUrls
|
||||
*/
|
||||
object JetsenxSdkUtil {
|
||||
|
||||
private const val TAG = "JetsenxSdkUtil"
|
||||
|
||||
// 渠道配置
|
||||
private const val AGCODE = "1100101010200492358120169"
|
||||
private const val TENANT = "huashitv"
|
||||
private const val ACCESSER = "JSTASJD"
|
||||
private const val SECRET = "kMeWsBu0QsTXpnft"
|
||||
private const val SERVICE_COMBO_CODE = "1100121010200494755850172"
|
||||
private const val FALLBACK_MAC = "CC:8C:DA:C6:E9:9B"
|
||||
|
||||
private var productCodeSp: String? = null
|
||||
private var isInitialized = false
|
||||
|
||||
interface InitCallback {
|
||||
fun onSuccess(memberCode: String)
|
||||
fun onFail(message: String)
|
||||
}
|
||||
|
||||
interface VideoUrlCallback {
|
||||
fun onSuccess(videoUrl1080: String, videoUrl720: String, isFree: Boolean)
|
||||
fun onError(code: String, message: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 SDK
|
||||
* @param context 上下文
|
||||
* @param uid thirdCardNo(天卡权益接口返回)
|
||||
* @param mac 设备 MAC(由调用方传入,为空则从 DeviceInfoUtil 获取)
|
||||
* @param callback 初始化回调
|
||||
*/
|
||||
fun init(context: Context, uid: String = "test_user", mac: String? = null, callback: InitCallback? = null) {
|
||||
// 优先用传入的 mac,其次从 DeviceInfoUtil 获取,最后用 FALLBACK_MAC
|
||||
val finalMac = when {
|
||||
!mac.isNullOrEmpty() -> mac
|
||||
else -> {
|
||||
val identity = com.pukka.mediaapp.utils.DeviceInfoUtil.getDeviceIdentity(context)
|
||||
identity.mac ?: FALLBACK_MAC
|
||||
}
|
||||
}
|
||||
val finalUid = if (TextUtils.isEmpty(uid)) finalMac else uid
|
||||
val utype = if (finalUid.matches(Regex("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"))) "3" else "2"
|
||||
|
||||
Log.d(TAG, "init: agcode=$AGCODE mac=$finalMac uid=$finalUid tenant=$TENANT utype=$utype")
|
||||
FileLogger.log(TAG, "init: agcode=$AGCODE mac=$finalMac uid=$finalUid tenant=$TENANT utype=$utype")
|
||||
|
||||
try {
|
||||
JetsenxSdk.init(
|
||||
context, AGCODE, finalMac, finalUid,
|
||||
TENANT, SECRET, ACCESSER, utype, "",
|
||||
SERVICE_COMBO_CODE,
|
||||
object : JetsenxInitCallback {
|
||||
override fun onInitSuccess(memberCode: String, p1: Boolean) {
|
||||
Log.d(TAG, "onInitSuccess: memberCode=$memberCode")
|
||||
FileLogger.log(TAG, "SDK init成功: memberCode=$memberCode")
|
||||
isInitialized = true
|
||||
callback?.onSuccess(memberCode)
|
||||
}
|
||||
|
||||
override fun onFail(code: String, msg: String) {
|
||||
Log.e(TAG, "onInitFail: code=$code msg=$msg")
|
||||
FileLogger.error(TAG, "SDK init失败: code=$code msg=$msg")
|
||||
isInitialized = false
|
||||
callback?.onFail(if (msg.isNotEmpty()) msg else "SDK初始化失败")
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "init exception", e)
|
||||
callback?.onFail("SDK初始化异常: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频播放地址
|
||||
* @param parentContentType "8"=免费, 其他=付费
|
||||
* @param programCode 节目编码
|
||||
*/
|
||||
fun getAllVideoUrl(
|
||||
parentContentType: String,
|
||||
programCode: String,
|
||||
callback: VideoUrlCallback
|
||||
) {
|
||||
Log.d(TAG, "getAllVideoUrl: parentContentType=$parentContentType programCode=$programCode")
|
||||
FileLogger.log(TAG, "getAllVideoUrl: parentContentType=$parentContentType programCode=$programCode")
|
||||
|
||||
if (TextUtils.isEmpty(programCode)) {
|
||||
callback.onError("-1", "programCode is empty")
|
||||
return
|
||||
}
|
||||
|
||||
getAllVideoUrlNext(parentContentType, programCode, callback)
|
||||
}
|
||||
|
||||
private fun getAllVideoUrlNext(
|
||||
parentContentType: String,
|
||||
programCode: String,
|
||||
callback: VideoUrlCallback
|
||||
) {
|
||||
val cachedProductCode = productCodeSp
|
||||
if (!cachedProductCode.isNullOrEmpty()) {
|
||||
getAllVideoUrl2(parentContentType, cachedProductCode, programCode, callback)
|
||||
} else {
|
||||
JetsenxSdk.getAllAssetPrices(ACCESSER, SECRET, object : JetsenxPricesCallback {
|
||||
override fun onAllAssetPrices(list: MutableList<Map<String, Any>>?) {
|
||||
FileLogger.log(TAG, "getAllAssetPrices返回: count=${list?.size}")
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
list.forEachIndexed { index, map ->
|
||||
FileLogger.log(TAG, " assetPrice[$index]: $map")
|
||||
}
|
||||
val productCode = list[0]["packageCode"]?.toString() ?: ""
|
||||
Log.d(TAG, "onAllAssetPrices: productCode=$productCode")
|
||||
FileLogger.log(TAG, "使用productCode=$productCode")
|
||||
productCodeSp = productCode
|
||||
getAllVideoUrl2(parentContentType, productCode, programCode, callback)
|
||||
} else {
|
||||
callback.onError("-2", "productCode is NULL")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code: String, msg: String) {
|
||||
Log.e(TAG, "getAllAssetPrices onFail: code=$code msg=$msg")
|
||||
FileLogger.error(TAG, "getAllAssetPrices失败: code=$code msg=$msg")
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllVideoUrl2(
|
||||
parentContentType: String,
|
||||
productCode: String,
|
||||
programCode: String,
|
||||
callback: VideoUrlCallback
|
||||
) {
|
||||
if (TextUtils.isEmpty(parentContentType)) {
|
||||
callback.onError("-1", "parentContentType is NULL")
|
||||
return
|
||||
}
|
||||
if (parentContentType != "8") {
|
||||
getVideoUrls2(productCode, programCode, callback)
|
||||
} else {
|
||||
getFreeVideoUrls2(programCode, callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVideoUrls2(productCode: String, programCode: String, callback: VideoUrlCallback) {
|
||||
JetsenxSdk.getVideoUrls(productCode, programCode, object : JetsenxUrlCallback {
|
||||
override fun onVideoUrls(videoInfos: Array<VideoInfo>?) {
|
||||
FileLogger.log(TAG, "getVideoUrls返回: count=${videoInfos?.size}")
|
||||
if (videoInfos != null && videoInfos.isNotEmpty()) {
|
||||
val (url1080, url720) = extractUrls(videoInfos)
|
||||
if (url1080.isNotEmpty() || url720.isNotEmpty()) {
|
||||
callback.onSuccess(url1080, url720, false)
|
||||
} else {
|
||||
callback.onError("411", "未返回播放地址")
|
||||
}
|
||||
} else {
|
||||
callback.onError("411", "未返回播放地址")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code: String, msg: String) {
|
||||
FileLogger.error(TAG, "getVideoUrls(付费)失败: code=$code msg=$msg")
|
||||
if (msg.contains("未订购") || code == "-110") {
|
||||
getFreeVideoUrls2(programCode, callback)
|
||||
} else {
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getFreeVideoUrls2(programCode: String, callback: VideoUrlCallback) {
|
||||
JetsenxSdk.getFreeVideoUrls(programCode, object : JetsenxUrlCallback {
|
||||
override fun onVideoUrls(videoInfos: Array<VideoInfo>?) {
|
||||
FileLogger.log(TAG, "getFreeVideoUrls返回: count=${videoInfos?.size}")
|
||||
if (videoInfos != null && videoInfos.isNotEmpty()) {
|
||||
val (url1080, url720) = extractUrls(videoInfos)
|
||||
if (url1080.isNotEmpty() || url720.isNotEmpty()) {
|
||||
callback.onSuccess(url1080, url720, true)
|
||||
} else {
|
||||
callback.onError("410", "未返回播放地址")
|
||||
}
|
||||
} else {
|
||||
callback.onError("410", "未返回播放地址")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code: String, msg: String) {
|
||||
FileLogger.error(TAG, "getFreeVideoUrls(免费)失败: code=$code msg=$msg")
|
||||
// 免费接口失败时,回退尝试付费接口
|
||||
val cachedProductCode = productCodeSp
|
||||
if (!cachedProductCode.isNullOrEmpty()) {
|
||||
FileLogger.log(TAG, "免费接口失败,回退尝试付费接口 getVideoUrls")
|
||||
JetsenxSdk.getVideoUrls(cachedProductCode, programCode, object : JetsenxUrlCallback {
|
||||
override fun onVideoUrls(videoInfos: Array<VideoInfo>?) {
|
||||
FileLogger.log(TAG, "回退getVideoUrls返回: count=${videoInfos?.size}")
|
||||
if (videoInfos != null && videoInfos.isNotEmpty()) {
|
||||
val (url1080, url720) = extractUrls(videoInfos)
|
||||
if (url1080.isNotEmpty() || url720.isNotEmpty()) {
|
||||
callback.onSuccess(url1080, url720, false)
|
||||
} else {
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
} else {
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(code2: String, msg2: String) {
|
||||
FileLogger.error(TAG, "回退getVideoUrls也失败: code=$code2 msg=$msg2")
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
callback.onError(code, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun extractUrls(videoInfos: Array<VideoInfo>): Pair<String, String> {
|
||||
var url1080 = ""
|
||||
var url720 = ""
|
||||
for (info in videoInfos) {
|
||||
val format = info.format ?: ""
|
||||
if (format.contains("720") && url720.isEmpty()) {
|
||||
url720 = info.videoUrl ?: ""
|
||||
} else if (format.contains("1080") && url1080.isEmpty()) {
|
||||
url1080 = info.videoUrl ?: ""
|
||||
}
|
||||
}
|
||||
FileLogger.log(TAG, "extractUrls: 1080=$url1080 720=$url720")
|
||||
return Pair(url1080, url720)
|
||||
}
|
||||
|
||||
private fun getMacAddress(context: Context): String {
|
||||
return try {
|
||||
val wifiManager = context.applicationContext
|
||||
.getSystemService(Context.WIFI_SERVICE) as android.net.wifi.WifiManager
|
||||
val wifiInfo = wifiManager.connectionInfo
|
||||
val mac = wifiInfo.macAddress
|
||||
if (mac.isNullOrEmpty() || mac == "02:00:00:00:00:00") "" else mac
|
||||
} catch (e: Exception) { "" }
|
||||
}
|
||||
|
||||
fun isReady(): Boolean = isInitialized
|
||||
}
|
||||
Reference in New Issue
Block a user