270 lines
11 KiB
Kotlin
270 lines
11 KiB
Kotlin
|
|
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
|
|||
|
|
}
|