diff --git a/README.md b/README.md index 293ea2a..6e9abd4 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,15 @@ ## 更新日志 +### v1.6.2(通用) + +* 修复按键无效的问题 +* 新的频道列表样式 + +### v1.6.1(安卓5及以上专用) + +* 增加凤凰卫视 + ### v1.6.0(通用) * 通用(春晚緊急修復) @@ -199,9 +208,8 @@ adb install my-tv.apk * 凤凰卫视、凤凰资讯台 * 海外 * 1.5.0 无法安装,1.5.1 可以安装 -* 选中的图标比例能否相差更大 -* 自动重连 * 节目增加预告 +* 频道列表优化 ## 赞赏 diff --git a/app/src/main/cpp/armeabi-v7a/libnative.so b/app/src/main/cpp/armeabi-v7a/libnative.so index c7288c4..cff7716 100755 Binary files a/app/src/main/cpp/armeabi-v7a/libnative.so and b/app/src/main/cpp/armeabi-v7a/libnative.so differ diff --git a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt index 099a918..cd6dbf4 100644 --- a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt +++ b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt @@ -47,6 +47,11 @@ class CardPresenter( .centerInside() .into(cardView.mainImageView) + "兵团卫视" -> Glide.with(viewHolder.view.context) + .load(R.drawable.bingtuan) + .centerInside() + .into(cardView.mainImageView) + else -> Glide.with(viewHolder.view.context) .load(tvViewModel.logo.value) .centerInside() diff --git a/app/src/main/java/com/lizongying/mytv/ConfirmationDialogFragment.kt b/app/src/main/java/com/lizongying/mytv/ConfirmationDialogFragment.kt new file mode 100644 index 0000000..2100510 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/ConfirmationDialogFragment.kt @@ -0,0 +1,36 @@ +package com.lizongying.mytv + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment + +class ConfirmationDialogFragment(private val listener: ConfirmationDialogListener) : + DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return activity?.let { + val builder = AlertDialog.Builder(it) + builder.setTitle("确认") + .setMessage("确认更新吗?") + .setPositiveButton( + "确定" + ) { _, _ -> + listener.onConfirm() + } + .setNegativeButton( + "取消" + ) { _, _ -> + listener.onCancel() + } + // 创建并返回 AlertDialog 对象 + builder.create() + } ?: throw IllegalStateException("Activity cannot be null") + } + + interface ConfirmationDialogListener { + fun onConfirm() + fun onCancel() + } +} + diff --git a/app/src/main/java/com/lizongying/mytv/Encryptor.kt b/app/src/main/java/com/lizongying/mytv/Encryptor.kt index 525a651..3d3fe99 100644 --- a/app/src/main/java/com/lizongying/mytv/Encryptor.kt +++ b/app/src/main/java/com/lizongying/mytv/Encryptor.kt @@ -9,6 +9,8 @@ class Encryptor { external fun hash(data: ByteArray): ByteArray? + external fun hash2(data: ByteArray): ByteArray? + companion object { init { System.loadLibrary("native") diff --git a/app/src/main/java/com/lizongying/mytv/MainActivity.kt b/app/src/main/java/com/lizongying/mytv/MainActivity.kt index 6f049a5..1006e7b 100644 --- a/app/src/main/java/com/lizongying/mytv/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv/MainActivity.kt @@ -27,10 +27,11 @@ import java.security.MessageDigest class MainActivity : FragmentActivity() { - var playerFragment = PlayerFragment() - private val mainFragment = MainFragment() - private val infoFragment = InfoFragment() - private val channelFragment = ChannelFragment() + private var ready = 0 + private var playerFragment = PlayerFragment() + private var mainFragment = MainFragment() + private var infoFragment = InfoFragment() + private var channelFragment = ChannelFragment() private lateinit var settingFragment: SettingFragment private var doubleBackToExitPressedOnce = false @@ -127,7 +128,7 @@ class MainActivity : FragmentActivity() { if (mainFragment.isHidden) { transaction.show(mainFragment) - keepRunnable() + mainActive() } else { transaction.hide(mainFragment) } @@ -135,11 +136,16 @@ class MainActivity : FragmentActivity() { transaction.commit() } - fun keepRunnable() { + fun mainActive() { handler.removeCallbacks(hideMain) handler.postDelayed(hideMain, delayHideMain) } + fun settingActive() { + handler.removeCallbacks(hideSetting) + handler.postDelayed(hideSetting, delayHideSetting) + } + private val hideMain = Runnable { if (!mainFragment.isHidden) { supportFragmentManager.beginTransaction().hide(mainFragment).commit() @@ -159,7 +165,11 @@ class MainActivity : FragmentActivity() { } fun fragmentReady() { - mainFragment.fragmentReady() + ready++ + Log.i(TAG, "ready $ready") + if (ready == 4) { + mainFragment.fragmentReady() + } } override fun onTouchEvent(event: MotionEvent?): Boolean { @@ -218,8 +228,7 @@ class MainActivity : FragmentActivity() { Log.i(TAG, "settingFragment ${settingFragment.isVisible}") if (!settingFragment.isVisible) { settingFragment.show(supportFragmentManager, "setting") - handler.removeCallbacks(hideSetting) - handler.postDelayed(hideSetting, delayHideSetting) + settingActive() } else { handler.removeCallbacks(hideSetting) settingFragment.dismiss() @@ -497,4 +506,4 @@ class MainActivity : FragmentActivity() { private companion object { const val TAG = "MainActivity" } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/MainFragment.kt b/app/src/main/java/com/lizongying/mytv/MainFragment.kt index 2124df6..2fff3bf 100644 --- a/app/src/main/java/com/lizongying/mytv/MainFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/MainFragment.kt @@ -38,8 +38,6 @@ class MainFragment : BrowseSupportFragment() { private val handler = Handler(Looper.getMainLooper()) private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable - private var ready = 0 - override fun onCreate(savedInstanceState: Bundle?) { Log.i(TAG, "onCreate") super.onCreate(savedInstanceState) @@ -85,6 +83,7 @@ class MainFragment : BrowseSupportFragment() { tvViewModel.change.observe(viewLifecycleOwner) { _ -> if (tvViewModel.change.value != null) { val title = tvViewModel.title.value + Log.i(TAG, "switch $title") if (tvViewModel.pid.value != "") { Log.i(TAG, "request $title") lifecycleScope.launch(Dispatchers.IO) { @@ -109,7 +108,7 @@ class MainFragment : BrowseSupportFragment() { } } - fragmentReady() + (activity as MainActivity).fragmentReady() } fun toLastPosition() { @@ -219,7 +218,7 @@ class MainFragment : BrowseSupportFragment() { ) { if (item is TVViewModel) { tvListViewModel.setItemPositionCurrent(item.id.value!!) - (activity as MainActivity).keepRunnable() + (activity as MainActivity).mainActive() } } } @@ -241,12 +240,8 @@ class MainFragment : BrowseSupportFragment() { } fun fragmentReady() { - ready++ - Log.i(TAG, "ready $ready") - if (ready == 4) { // request.fetchPage() - tvListViewModel.getTVViewModel(itemPosition)?.changed() - } + tvListViewModel.getTVViewModel(itemPosition)?.changed() } fun play(itemPosition: Int) { @@ -325,7 +320,9 @@ class MainFragment : BrowseSupportFragment() { override fun onDestroy() { Log.i(TAG, "onDestroy") super.onDestroy() - handler.removeCallbacks(mUpdateProgramRunnable) + if (::mUpdateProgramRunnable.isInitialized) { + handler.removeCallbacks(mUpdateProgramRunnable) + } } companion object { diff --git a/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt b/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt index b7b987b..81f7ccc 100644 --- a/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt @@ -80,6 +80,7 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback { super.onPlayerError(error) Log.e(TAG, "PlaybackException $error") + tvViewModel?.changed() } }) } @@ -107,10 +108,12 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback { if (playerView != null && playerView!!.player?.isPlaying == false) { Log.i(TAG, "replay") playerView!!.player?.prepare() + playerView!!.player?.play() } if (exoPlayer?.isPlaying == false) { Log.i(TAG, "replay") exoPlayer?.prepare() + exoPlayer?.play() } } diff --git a/app/src/main/java/com/lizongying/mytv/Request.kt b/app/src/main/java/com/lizongying/mytv/Request.kt index 4055130..ecc2aed 100644 --- a/app/src/main/java/com/lizongying/mytv/Request.kt +++ b/app/src/main/java/com/lizongying/mytv/Request.kt @@ -7,6 +7,10 @@ import android.util.Base64 import android.util.Log import com.lizongying.mytv.Utils.getDateFormat import com.lizongying.mytv.api.ApiClient +import com.lizongying.mytv.api.Auth +import com.lizongying.mytv.api.AuthRequest +import com.lizongying.mytv.api.FAuth +import com.lizongying.mytv.api.FAuthService import com.lizongying.mytv.api.Info import com.lizongying.mytv.api.LiveInfo import com.lizongying.mytv.api.LiveInfoRequest @@ -31,9 +35,12 @@ class Request { private var yspApiService: YSPApiService = ApiClient().yspApiService private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService private var yspProtoService: YSPProtoService = ApiClient().yspProtoService + private var fAuthService: FAuthService = ApiClient().fAuthService private var ysp: YSP? = null private var token = "" + private var needAuth = false + // TODO onDestroy private val handler = Handler(Looper.getMainLooper()) private lateinit var btraceRunnable: BtraceRunnable @@ -52,6 +59,84 @@ class Request { } var call: Call? = null + private var callAuth: Call? = null + + private fun fetchAuth(tvModel: TVViewModel, cookie: String) { + callAuth?.cancel() + + val title = tvModel.title.value + + val data = ysp?.getAuthData(tvModel) + val request = data?.let { AuthRequest(it) } + callAuth = request?.let { yspApiService.getAuth("guid=${ysp?.getGuid()}; $cookie", it) } + + callAuth?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + val liveInfo = response.body() + + if (liveInfo?.data?.token != null) { + Log.i(TAG, "token ${liveInfo.data.token}") + ysp?.token = liveInfo.data.token + fetchVideo(tvModel, cookie) + } else { + Log.e(TAG, "$title token error") + if (tvModel.retryTimes < tvModel.retryMaxTimes) { + tvModel.retryTimes++ + if (tvModel.getTV().needToken) { + if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { + if (!tvModel.getTV().mustToken) { + fetchAuth(tvModel, cookie) + } + } else { + token = "" + fetchAuth(tvModel) + } + } else { + fetchAuth(tvModel, cookie) + } + } + } + } else { + Log.e(TAG, "$title auth status error") + if (tvModel.retryTimes < tvModel.retryMaxTimes) { + tvModel.retryTimes++ + if (tvModel.getTV().needToken) { + if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { + if (!tvModel.getTV().mustToken) { + fetchAuth(tvModel, cookie) + } + } else { + token = "" + fetchAuth(tvModel) + } + } else { + fetchAuth(tvModel, cookie) + } + } + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.e(TAG, "$title auth request error $t") + if (tvModel.retryTimes < tvModel.retryMaxTimes) { + tvModel.retryTimes++ + if (tvModel.getTV().needToken) { + if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { + if (!tvModel.getTV().mustToken) { + fetchAuth(tvModel, cookie) + } + } else { + token = "" + fetchAuth(tvModel) + } + } else { + fetchAuth(tvModel, cookie) + } + } + } + }) + } fun fetchVideo(tvModel: TVViewModel, cookie: String) { call?.cancel() @@ -64,7 +149,13 @@ class Request { tvModel.seq = 0 val data = ysp?.switch(tvModel) val request = data?.let { LiveInfoRequest(it) } - call = request?.let { yspApiService.getLiveInfo("guid=${ysp?.getGuid()}; $cookie", it) } + call = request?.let { + yspApiService.getLiveInfo( + "guid=${ysp?.getGuid()}; $cookie", + ysp!!.token, + it + ) + } call?.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { @@ -101,13 +192,16 @@ class Request { if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { if (!tvModel.getTV().mustToken) { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } else { token = "" fetchVideo(tvModel) +// fetchAuth(tvModel) } } else { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } } @@ -123,13 +217,16 @@ class Request { if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { if (!tvModel.getTV().mustToken) { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } else { token = "" fetchVideo(tvModel) +// fetchAuth(tvModel) } } else { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } } @@ -142,20 +239,23 @@ class Request { if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { if (!tvModel.getTV().mustToken) { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } else { token = "" fetchVideo(tvModel) +// fetchAuth(tvModel) } } else { fetchVideo(tvModel, cookie) +// fetchAuth(tvModel, cookie) } } } } override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "$title request error $t") + Log.e(TAG, "$title fetchVideo request error $t") if (tvModel.retryTimes < tvModel.retryMaxTimes) { tvModel.retryTimes++ if (tvModel.getTV().needToken) { @@ -175,16 +275,63 @@ class Request { }) } + fun fetchAuth(tvModel: TVViewModel) { + if (token == "") { + yspTokenService.getInfo("") + .enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + token = response.body()?.data?.token!! + Log.i(TAG, "info success $token") + val cookie = + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" + fetchAuth(tvModel, cookie) + } else { + Log.e(TAG, "info status error") + if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { + tvModel.tokenRetryTimes++ + fetchAuth(tvModel) + } else { + if (!tvModel.getTV().mustToken) { + val cookie = + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" + fetchAuth(tvModel, cookie) + } + } + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.e(TAG, "info request error $t") + if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { + tvModel.tokenRetryTimes++ + fetchVideo(tvModel) + } else { + if (!tvModel.getTV().mustToken) { + val cookie = + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" + fetchAuth(tvModel, cookie) + } + } + } + }) + } else { + val cookie = + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" + fetchAuth(tvModel, cookie) + } + } + fun fetchVideo(tvModel: TVViewModel) { if (token == "") { - yspTokenService.getInfo() + yspTokenService.getInfo("") .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful && response.body()?.data?.token != null) { token = response.body()?.data?.token!! Log.i(TAG, "info success $token") val cookie = - "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" fetchVideo(tvModel, cookie) } else { Log.e(TAG, "info status error") @@ -194,7 +341,7 @@ class Request { } else { if (!tvModel.getTV().mustToken) { val cookie = - "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109" + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" fetchVideo(tvModel, cookie) } } @@ -209,7 +356,7 @@ class Request { } else { if (!tvModel.getTV().mustToken) { val cookie = - "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109" + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" fetchVideo(tvModel, cookie) } } @@ -217,18 +364,67 @@ class Request { }) } else { val cookie = - "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" fetchVideo(tvModel, cookie) } } + private var fAuth: Call? = null + fun fetchFAuth(tvModel: TVViewModel) { + call?.cancel() + callAuth?.cancel() + fAuth?.cancel() + + val title = tvModel.title.value + + fAuth = fAuthService.getAuth(tvModel.getTV().pid, "HD") + fAuth?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful && response.body()?.data?.live_url != null) { + val url = response.body()?.data?.live_url!! +// Log.d(TAG, "$title url $url") + tvModel.addVideoUrl(url) + tvModel.allReady() + tvModel.retryTimes = 0 + } else { + Log.e(TAG, "auth status error") + if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { + tvModel.tokenRetryTimes++ + fetchFAuth(tvModel) + } + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.e(TAG, "auth request error $t") + if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { + tvModel.tokenRetryTimes++ + fetchFAuth(tvModel) + } + } + }) + } + fun fetchData(tvModel: TVViewModel) { + if (tvModel.getTV().channel == "港澳台") { + fetchFAuth(tvModel) + return + } + if (tvModel.getTV().needToken) { - fetchVideo(tvModel) + if (needAuth) { + fetchAuth(tvModel) + } else { + fetchVideo(tvModel) + } } else { val cookie = - "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109" - fetchVideo(tvModel, cookie) + "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205" + if (needAuth) { + fetchAuth(tvModel, cookie) + } else { + fetchVideo(tvModel, cookie) + } } } @@ -240,10 +436,10 @@ class Request { } fun fetchToken() { - yspTokenService.getInfo() + yspTokenService.getInfo(token) .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && response.body()?.data?.token != null) { + if (response.isSuccessful) { token = response.body()?.data?.token!! Log.i(TAG, "info success $token") } else { diff --git a/app/src/main/java/com/lizongying/mytv/SettingFragment.kt b/app/src/main/java/com/lizongying/mytv/SettingFragment.kt index ef3844c..801f279 100644 --- a/app/src/main/java/com/lizongying/mytv/SettingFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/SettingFragment.kt @@ -8,16 +8,20 @@ import androidx.fragment.app.DialogFragment import com.lizongying.mytv.databinding.DialogBinding -class SettingFragment(private val versionName: String, - private val channelReversal: Boolean, - private val channelNum: Boolean, - private val bootStartup: Boolean, - ) : +class SettingFragment( + private val versionName: String, + private val versionCode: Long, + private val channelReversal: Boolean, + private val channelNum: Boolean, + private val bootStartup: Boolean, +) : DialogFragment() { private var _binding: DialogBinding? = null private val binding get() = _binding!! + private lateinit var updateManager: UpdateManager + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setStyle(STYLE_NO_TITLE, 0) @@ -36,28 +40,50 @@ class SettingFragment(private val versionName: String, switchChannelReversal?.isChecked = channelReversal switchChannelReversal?.setOnCheckedChangeListener { _, isChecked -> SP.channelReversal = isChecked + (activity as MainActivity).settingActive() } val switchChannelNum = _binding?.switchChannelNum switchChannelNum?.isChecked = channelNum switchChannelNum?.setOnCheckedChangeListener { _, isChecked -> SP.channelNum = isChecked + (activity as MainActivity).settingActive() } val switchBootStartup = _binding?.switchBootStartup switchBootStartup?.isChecked = bootStartup switchBootStartup?.setOnCheckedChangeListener { _, isChecked -> SP.bootStartup = isChecked + (activity as MainActivity).settingActive() } + updateManager = UpdateManager(context, this, versionCode) + _binding?.checkVersion?.setOnClickListener(OnClickListenerCheckVersion(updateManager)) + return binding.root } + fun setVersionName(versionName: String) { + binding.versionName.text = versionName + } + + internal class OnClickListenerCheckVersion(private val updateManager: UpdateManager) : + View.OnClickListener { + override fun onClick(view: View?) { + updateManager.checkAndUpdate() + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null } + override fun onDestroy() { + super.onDestroy() +// updateManager.destroy() + } + companion object { const val TAG = "SettingFragment" } diff --git a/app/src/main/java/com/lizongying/mytv/TVList.kt b/app/src/main/java/com/lizongying/mytv/TVList.kt index 6eeb17a..966ee72 100644 --- a/app/src/main/java/com/lizongying/mytv/TVList.kt +++ b/app/src/main/java/com/lizongying/mytv/TVList.kt @@ -9,14 +9,14 @@ import java.io.IOException object TVList { var list = mapOf( - "央视频道" to listOf( + "央视" to listOf( TV( 0, "CCTV1 综合", "CCTV1", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp", "600001859", "2022576801", @@ -30,7 +30,7 @@ object TVList { "CCTV2", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp", "600001800", "2022576701", @@ -44,7 +44,7 @@ object TVList { "CCTV3", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp", "600001801", "2022576501", @@ -61,7 +61,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8" ), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp", "600001814", "2022576601", @@ -75,7 +75,7 @@ object TVList { "CCTV5", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226395/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp", "600001818", "2022576401", @@ -89,7 +89,7 @@ object TVList { "CCTV6", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226393/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp", "600108442", "2013693901", @@ -103,7 +103,7 @@ object TVList { "CCTV7", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226192/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp", "600004092", "2022576201", @@ -117,7 +117,7 @@ object TVList { "CCTV8", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226391/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp", "600001803", "2022576101", @@ -131,7 +131,7 @@ object TVList { "CCTV9", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp", "600004078", "2022576001", @@ -145,7 +145,7 @@ object TVList { "CCTV10", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226189/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp", "600001805", "2022573001", @@ -159,7 +159,7 @@ object TVList { "CCTV11", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226240/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp", "600001806", "2022575901", @@ -173,7 +173,7 @@ object TVList { "CCTV12", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226190/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp", "600001807", "2022575801", @@ -190,7 +190,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226233/index.m3u8" ), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp", "600001811", "2022575701", @@ -204,7 +204,7 @@ object TVList { "CCTV14", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp", "600001809", "2022575601", @@ -218,7 +218,7 @@ object TVList { "CCTV15", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp", "600001815", "2022575501", @@ -235,7 +235,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8" ), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp", "600098637", "2022575401", @@ -249,7 +249,7 @@ object TVList { "CCTV17", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp", "600001810", "2022575301", @@ -263,7 +263,7 @@ object TVList { "CCTV5+", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226221/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp", "600001817", "2022576301", @@ -277,7 +277,7 @@ object TVList { "CCTV4K", listOf(), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp", "600002264", "2022575201", @@ -291,7 +291,7 @@ object TVList { "CCTV8K", listOf(), 0, - "央视频道", + "央视", "", "600156816", "2020603421", @@ -305,7 +305,7 @@ object TVList { "CCTV风云剧场频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp", "600099658", "2012513603", @@ -319,7 +319,7 @@ object TVList { "CCTV第一剧场频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp", "600099655", "2012514403", @@ -333,7 +333,7 @@ object TVList { "CCTV怀旧剧场频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp", "600099620", "2012511203", @@ -347,7 +347,7 @@ object TVList { "CCTV世界地理频道", listOf(), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/bb3c6c9e145d698137f5bb64a582021a01b51344b929003630eb769ea65832a9.png?imageMogr2/format/webp", "600099637", "2012513303", @@ -361,7 +361,7 @@ object TVList { "CCTV风云音乐频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226953/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp", "600099660", "2012514103", @@ -375,7 +375,7 @@ object TVList { "CCTV兵器科技频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp", "600099649", "2012513403", @@ -389,7 +389,7 @@ object TVList { "CCTV风云足球频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp", "600099636", "2012514203", @@ -403,7 +403,7 @@ object TVList { "CCTV高尔夫·网球频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp", "600099659", "2012512503", @@ -417,7 +417,7 @@ object TVList { "CCTV女性时尚频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226969/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp", "600099650", "2012513903", @@ -431,7 +431,7 @@ object TVList { "CCTV央视文化精品频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp", "600099653", "2012513803", @@ -445,7 +445,7 @@ object TVList { "CCTV央视台球频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226956/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp", "600099652", "2012513703", @@ -459,7 +459,7 @@ object TVList { "CCTV电视指南频道", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8"), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp", "600099656", "2012514003", @@ -473,7 +473,7 @@ object TVList { "CCTV卫生健康频道", listOf(), 0, - "央视频道", + "央视", "https://resources.yangshipin.cn/assets/oms/image/202306/54a6863656fdfd8f803be193ddf22441c5000a108833889816fd2d8911715ce8.png?imageMogr2/format/webp", "600099651", "2012513503", @@ -482,14 +482,14 @@ object TVList { mustToken = true ), ), - "地方频道" to listOf( + "地方" to listOf( TV( 33, "东方卫视", "东方卫视", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226217/index.m3u8"), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp", "600002483", "2000292403", @@ -506,7 +506,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226211/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp", "600002475", "2000296203", @@ -523,7 +523,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226194/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp", "600002508", "2000294503", @@ -540,7 +540,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226201/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp", "600002505", "2000281303", @@ -557,7 +557,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp", "600002521", "2000295603", @@ -574,7 +574,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225764/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp", "600002503", "2000294103", @@ -588,7 +588,7 @@ object TVList { "山东卫视", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226209/index.m3u8"), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp", "600002513", "2000294803", @@ -602,7 +602,7 @@ object TVList { "广东卫视", listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226216/index.m3u8"), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp", "600002485", "2000292703", @@ -619,7 +619,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225770/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp", "600002509", "2000294203", @@ -636,7 +636,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226202/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/657651f411de2673d1770d9a78b44c1265704f7468cc41d4be7f51d630768494.png?imageMogr2/format/webp", "600002531", "2000297803", @@ -653,7 +653,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225767/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp", "600002525", "2000296103", @@ -670,7 +670,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225750/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp", "600002493", "2000293403", @@ -687,7 +687,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225793/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp", "600002490", "2000293303", @@ -704,7 +704,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226222/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp", "600002309", "2000272103", @@ -721,7 +721,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226215/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp", "600002498", "2000293903", @@ -738,7 +738,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226199/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp", "600002520", "2000295503", @@ -755,7 +755,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226203/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp", "600002532", "2000298003", @@ -772,7 +772,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp", "600002481", "2000292203", @@ -789,7 +789,7 @@ object TVList { "http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp", "600002516", "2000295003", @@ -806,7 +806,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp", "600002484", "2000292503", @@ -823,7 +823,7 @@ object TVList { "http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8" ), 0, - "地方频道", + "地方", "https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp", "600002506", "2000291503", @@ -837,7 +837,7 @@ object TVList { "天津卫视", listOf(), 0, - "地方频道", + "地方", "", "600152137", "2019927003", @@ -851,7 +851,7 @@ object TVList { "新疆卫视", listOf(), 0, - "地方频道", + "地方", "", "600152138", "2019927403", @@ -859,15 +859,73 @@ object TVList { true, mustToken = false ), - ), - "国际频道" to listOf( TV( 56, + "兵团卫视", + "兵团卫视", + listOf(), + 0, + "地方", + "", + "600170344", + "2022606701", + "", + true, + mustToken = false + ), + ), + "港澳台" to listOf( + TV( + 57, + "凤凰卫视资讯台", + "", + listOf(), + 0, + "港澳台", + "http://c1.fengshows-cdn.com/a/2021_22/79dcc3a9da358a3.png", + "7c96b084-60e1-40a9-89c5-682b994fb680", + "", + "", + false, + mustToken = false + ), + TV( + 58, + "凤凰卫视中文台", + "", + listOf(), + 0, + "港澳台", + "http://c1.fengshows-cdn.com/a/2021_22/ede3d9e09be28e5.png", + "f7f48462-9b13-485b-8101-7b54716411ec", + "", + "", + false, + mustToken = false + ), + TV( + 59, + "凤凰卫视香港台", + "", + listOf(), + 0, + "港澳台", + "http://c1.fengshows-cdn.com/a/2021_23/325d941090bee17.png", + "15e02d92-1698-416c-af2f-3e9a872b4d78", + "", + "", + false, + mustToken = false + ), + ), + "国际" to listOf( + TV( + 60, "CGTN", "CGTN", listOf("http://live.cgtn.com/1000/prog_index.m3u8"), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp", "600014550", "2022575001", @@ -876,12 +934,12 @@ object TVList { mustToken = false ), TV( - 57, + 61, "CGTN 法语频道", "CGTN法语频道", listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp", "600084704", "2022574901", @@ -890,12 +948,12 @@ object TVList { mustToken = false ), TV( - 58, + 62, "CGTN 俄语频道", "CGTN俄语频道", listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp", "600084758", "2022574801", @@ -904,12 +962,12 @@ object TVList { mustToken = false ), TV( - 59, + 63, "CGTN 阿拉伯语频道", "CGTN阿拉伯语频道", listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp", "600084782", "2022574601", @@ -918,7 +976,7 @@ object TVList { mustToken = false ), TV( - 60, + 64, "CGTN 西班牙语频道", "CGTN西班牙语频道", listOf( @@ -926,7 +984,7 @@ object TVList { "http://livees.cgtn.com/1000e/prog_index.m3u8" ), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp", "600084744", "2022571701", @@ -935,12 +993,12 @@ object TVList { mustToken = false ), TV( - 61, + 65, "CGTN 纪录频道", "CGTN外语纪录频道", listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"), 0, - "国际频道", + "国际", "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp", "600084781", "2022574701", diff --git a/app/src/main/java/com/lizongying/mytv/UpdateManager.kt b/app/src/main/java/com/lizongying/mytv/UpdateManager.kt index 02b054a..ac1b852 100644 --- a/app/src/main/java/com/lizongying/mytv/UpdateManager.kt +++ b/app/src/main/java/com/lizongying/mytv/UpdateManager.kt @@ -1,5 +1,189 @@ package com.lizongying.mytv -class UpdateManager { +import android.app.DownloadManager +import android.app.DownloadManager.Request +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.database.Cursor +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.widget.Toast +import com.lizongying.mytv.api.Release +import com.lizongying.mytv.requests.MyRequest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.File + +class UpdateManager( + private var context: Context?, + private var settingFragment: SettingFragment, + private var versionCode: Long +) : + ConfirmationDialogFragment.ConfirmationDialogListener { + + private var myRequest = MyRequest() + private var release: Release? = null + + private var downloadReceiver: DownloadReceiver? = null + + fun checkAndUpdate() { + CoroutineScope(Dispatchers.Main).launch { + try { + release = myRequest.getRelease() + updateUI(release) + Log.i(TAG, "versionCode $versionCode ${release?.data?.versionCode}") + if (release != null) { + if (release?.data?.versionCode!! >= versionCode) { + val dialog = ConfirmationDialogFragment(this@UpdateManager) + dialog.show(settingFragment.fragmentManager, "ConfirmationDialogFragment") + } else { + Toast.makeText(context, "不需要更新", Toast.LENGTH_LONG) + .show() + } + } + } catch (e: Exception) { + Log.e(TAG, "Error occurred: ${e.message}", e) + } + } + } + + private fun updateUI(release: Release?) { + if (release?.data?.versionName.isNullOrEmpty()) { + settingFragment.setVersionName("版本获取失败") + } else { + settingFragment.setVersionName("最新版本:${release?.data?.versionName!!}") + } + } + + private fun startDownload(release: Release) { + val apkFileName = "my-tv-${release.data.versionName}.apk" + Log.i(TAG, "apkFileName $apkFileName") + val downloadManager = + context!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val request = Request(Uri.parse(release.data.downloadUrl)) + Log.i(TAG, "url ${Uri.parse(release.data.downloadUrl)}") + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkFileName) + request.setTitle("New Version Download") + request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + + // 获取下载任务的引用 + val downloadReference = downloadManager.enqueue(request) + + downloadReceiver = DownloadReceiver(context!!, apkFileName, downloadReference) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context!!.registerReceiver( + downloadReceiver, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), + Context.RECEIVER_NOT_EXPORTED, + ) + } else { + context!!.registerReceiver( + downloadReceiver, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) + ) + } + + getDownloadProgress(context!!, downloadReference) { progress -> + println("Download progress: $progress%") + } + } + + private fun getDownloadProgress(context: Context, downloadId: Long, progressListener: (Int) -> Unit) { + val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val handler = Handler(Looper.getMainLooper()) + val intervalMillis: Long = 1000 + + handler.post(object : Runnable { + override fun run() { + Log.i(TAG, "search") + val query = DownloadManager.Query().setFilterById(downloadId) + val cursor: Cursor = downloadManager.query(query) + cursor.use { + if (it.moveToFirst()) { + val bytesDownloadedIndex = + it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR) + val bytesTotalIndex = + it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES) + + // 检查列名是否存在 + if (bytesDownloadedIndex != -1 && bytesTotalIndex != -1) { + val bytesDownloaded = it.getInt(bytesDownloadedIndex) + val bytesTotal = it.getInt(bytesTotalIndex) + + if (bytesTotal != -1) { + val progress = (bytesDownloaded * 100L / bytesTotal).toInt() + progressListener(progress) + if (progress == 100) { + return + } + } + } + } + } + +// handler.postDelayed(this, intervalMillis) + } + }) + } + + private class DownloadReceiver( + private val context: Context, + private val apkFileName: String, + private val downloadReference: Long + ) : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) + Log.i(TAG, "reference $reference") + val progress = intent.getIntExtra("progress", 0) + Log.i(TAG, "progress $progress") + + // 检查是否是我们发起的下载 + if (reference == downloadReference) { + // 下载完成,触发安装 + installNewVersion() + } + } + + private fun installNewVersion() { + val installIntent = Intent(Intent.ACTION_VIEW) + val apkUri = Uri.fromFile( + File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), + apkFileName + ) + ) + installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive") + installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(installIntent) + } + } + + companion object { + private const val TAG = "UpdateManager" + } + + override fun onConfirm() { + Log.i(TAG, "onConfirm $release") + release?.let { startDownload(it) } + } + + override fun onCancel() { + } + + + fun destroy() { + if (downloadReceiver != null) { + context!!.unregisterReceiver(downloadReceiver) + Log.i(TAG, "destroy downloadReceiver") + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt b/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt index 076e6aa..fb22208 100644 --- a/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt +++ b/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt @@ -13,8 +13,10 @@ import javax.net.ssl.X509TrustManager class ApiClient { private val yspUrl = "https://player-api.yangshipin.cn/" private val myUrl = "https://lyrics.run/" + private val devUrl = "http://10.0.2.2:8081/" private val protoUrl = "https://capi.yangshipin.cn/" private val traceUrl = "https://btrace.yangshipin.cn/" + private val fUrl = "https://m.fengshows.com/" private var okHttpClient = getUnsafeOkHttpClient() @@ -34,6 +36,14 @@ class ApiClient { .build().create(YSPTokenService::class.java) } + val releaseService: ReleaseService by lazy { + Retrofit.Builder() + .baseUrl(myUrl) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build().create(ReleaseService::class.java) + } + val yspProtoService: YSPProtoService by lazy { Retrofit.Builder() .baseUrl(protoUrl) @@ -50,6 +60,14 @@ class ApiClient { .build().create(YSPBtraceService::class.java) } + val fAuthService: FAuthService by lazy { + Retrofit.Builder() + .baseUrl(fUrl) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build().create(FAuthService::class.java) + } + private fun getUnsafeOkHttpClient(): OkHttpClient { try { val trustAllCerts: Array = arrayOf( diff --git a/app/src/main/java/com/lizongying/mytv/api/Auth.kt b/app/src/main/java/com/lizongying/mytv/api/Auth.kt new file mode 100644 index 0000000..095516c --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/api/Auth.kt @@ -0,0 +1,37 @@ +package com.lizongying.mytv.api + +import android.util.Log +import okhttp3.MediaType +import okhttp3.RequestBody +import okio.BufferedSink +import java.io.IOException + +data class Auth( + val code: Int, + val msg: String, + val data: AuthData, +) + +data class AuthData( + val token: String, +) + +data class AuthRequest( + var data: String, +) : RequestBody() { + override fun contentType(): MediaType? { + return MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8") + } + + override fun writeTo(sink: BufferedSink) { + try { + sink.writeUtf8(data) + } catch (e: IOException) { + Log.e(TAG, "$e") + } + } + + companion object { + private const val TAG = "AuthRequest" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/FAuth.kt b/app/src/main/java/com/lizongying/mytv/api/FAuth.kt new file mode 100644 index 0000000..0dca043 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/api/FAuth.kt @@ -0,0 +1,10 @@ +package com.lizongying.mytv.api + + +data class FAuth( + val data: Data, +) { + data class Data( + val live_url: String, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/FAuthService.kt b/app/src/main/java/com/lizongying/mytv/api/FAuthService.kt new file mode 100644 index 0000000..1e597dd --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/api/FAuthService.kt @@ -0,0 +1,13 @@ +package com.lizongying.mytv.api + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface FAuthService { + @GET("api/v3/hub/live/auth-url") + fun getAuth( + @Query("live_id") live_id: String = "", + @Query("live_qa") live_qa: String = "", + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/Info.kt b/app/src/main/java/com/lizongying/mytv/api/Info.kt index c4a3eeb..c1898bc 100644 --- a/app/src/main/java/com/lizongying/mytv/api/Info.kt +++ b/app/src/main/java/com/lizongying/mytv/api/Info.kt @@ -1,14 +1,28 @@ package com.lizongying.mytv.api + data class Info( val code: Int?, val msg: String?, - val data: InfoData, -) + val data: Data, +) { + data class Data( + val token: String, + ) +} -data class InfoData( - val token: String, -) +data class Release( + val code: Int?, + val msg: String?, + val data: Data, +) { + data class Data( + val versionName: String, + val versionCode: Int, + val downloadUrl: String, + val updateTime: Int, + ) +} data class TimeResponse( val data: Time diff --git a/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt b/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt new file mode 100644 index 0000000..d06eea1 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt @@ -0,0 +1,11 @@ +package com.lizongying.mytv.api + +import retrofit2.Call +import retrofit2.http.GET + + +interface ReleaseService { + @GET("my-tv/v1/release") + fun getRelease( + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/YSP.kt b/app/src/main/java/com/lizongying/mytv/api/YSP.kt index 0bb4edf..de104f5 100644 --- a/app/src/main/java/com/lizongying/mytv/api/YSP.kt +++ b/app/src/main/java/com/lizongying/mytv/api/YSP.kt @@ -50,6 +50,9 @@ class YSP(var context: Context) { private var signature = "" + private var appid = "ysp_pc" + var token = "" + private var encryptor: Encryptor? = null init { @@ -80,6 +83,19 @@ class YSP(var context: Context) { return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}""" } + fun getAuthData(tvModel: TVViewModel): String { + livepid = tvModel.pid.value!! + + randStr = getRand() + + if (tvModel.retryTimes > 0) { + guid = newGuid() + } + + signature = getAuthSignature() + return """pid=$livepid&guid=$guid&appid=$appid&rand_str=$randStr&signature=$signature""" + } + private fun getTimeStr(): String { return getDateTimestamp().toString() } @@ -122,6 +138,13 @@ class YSP(var context: Context) { return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } } + private fun getAuthSignature(): String { + val e = + "appid=${appid}&guid=${guid}&pid=${livepid}&rand_str=${randStr}".toByteArray() + val hashedData = encryptor?.hash2(e) ?: return "" + return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } + } + companion object { private const val TAG = "YSP" } diff --git a/app/src/main/java/com/lizongying/mytv/api/YSPApiService.kt b/app/src/main/java/com/lizongying/mytv/api/YSPApiService.kt index b121d07..e9bbee2 100644 --- a/app/src/main/java/com/lizongying/mytv/api/YSPApiService.kt +++ b/app/src/main/java/com/lizongying/mytv/api/YSPApiService.kt @@ -13,9 +13,23 @@ interface YSPApiService { "content-type: application/json;charset=UTF-8", "referer: https://www.yangshipin.cn/", "yspappid: 519748109", + "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ) fun getLiveInfo( @Header("cookie") cookie: String, + @Header("Yspplayertoken") token: String, @Body request: LiveInfoRequest, ): Call + + @POST("v1/player/auth") + @Headers( + "content-type: application/x-www-form-urlencoded;charset=UTF-8", + "referer: https://www.yangshipin.cn/", + "yspappid: 519748109", + "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + ) + fun getAuth( + @Header("cookie") cookie: String, + @Body request: AuthRequest, + ): Call } \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt b/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt index 3188708..7d08cd1 100644 --- a/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt +++ b/app/src/main/java/com/lizongying/mytv/api/YSPTokenService.kt @@ -2,10 +2,12 @@ package com.lizongying.mytv.api import retrofit2.Call import retrofit2.http.GET +import retrofit2.http.Query interface YSPTokenService { @GET("my-tv/v1/info") fun getInfo( + @Query("token") token: String = "", ): Call } \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt b/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt new file mode 100644 index 0000000..412c504 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt @@ -0,0 +1,44 @@ +package com.lizongying.mytv.requests + +import com.lizongying.mytv.api.ApiClient +import com.lizongying.mytv.api.Release +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class MyRequest { + private var releaseService = ApiClient().releaseService + + suspend fun getRelease(): Release? { + return withContext(Dispatchers.IO) { + fetchRelease() + } + } + + private suspend fun fetchRelease(): Release? { + return suspendCoroutine { continuation -> + releaseService.getRelease() + .enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + continuation.resume(response.body()) + } else { + continuation.resume(null) + } + } + + override fun onFailure(call: Call, t: Throwable) { + continuation.resume(null) + } + }) + } + } + + companion object { + private const val TAG = "MyRequest" + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/channel.xml b/app/src/main/res/layout/channel.xml index 47c664a..7c48bd5 100644 --- a/app/src/main/res/layout/channel.xml +++ b/app/src/main/res/layout/channel.xml @@ -13,7 +13,6 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:layout_marginTop="0dp" - android:text="99" android:textColor="#FFEEEEEE" android:textStyle="bold" android:textSize="50sp" /> diff --git a/app/src/main/res/layout/dialog.xml b/app/src/main/res/layout/dialog.xml index 819e99d..bb69502 100644 --- a/app/src/main/res/layout/dialog.xml +++ b/app/src/main/res/layout/dialog.xml @@ -27,7 +27,31 @@ android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/" - > + /> + +