Compare commits

...
7 Commits
Author SHA1 Message Date
Li ZongYing aa4d52681f fix ysp 2024-02-07 03:05:11 +08:00
Li ZongYing 8137515605 fix ysp err 2024-02-05 06:25:31 +08:00
Li ZongYing 6c16408b90 upgrade player 2024-02-04 22:23:28 +08:00
Li ZongYing 44ab3a6669 fix channel number did not disappear 2024-02-03 23:30:37 +08:00
Li ZongYing 917b245bbe readme 2024-02-02 23:34:39 +08:00
Li ZongYing dd4550cf4f replay on resume 2024-02-01 18:10:58 +08:00
Li ZongYing 4bdc625718 add channel 2024-01-26 18:30:31 +08:00
26 changed files with 2331 additions and 514 deletions
+41 -3
View File
@@ -1,6 +1,6 @@
# 我的电视 # 我的电视
安卓电视直播软件,内置直播源 电视直播软件,安装即可使用
## 使用 ## 使用
@@ -14,10 +14,32 @@
## 更新日志 ## 更新日志
### v1.5.2(通用版)
* 修复APP恢复后频道号、频道列表不自动消失的问题
### v1.5.1(高版本专用)
* 性能优化
### v1.5.0(通用版)
* 修复部分情况下APP切换后无法继续播放的问题
### v1.4.9(高版本专用)
* 同步v1.4.8
### v1.4.8(通用版)
* 频道号从1开始,CCTV5+为18
* 提高CCTV6清晰度
* 增加天津卫视、新疆卫视
### v1.4.7(高版本专用) ### v1.4.7(高版本专用)
* 修复部分用户cctv13播放过程中卡住的问题 * 修复部分用户CCTV13播放过程中卡住的问题
* 调整cctv的频道顺序 * 调整CCTV的频道顺序
### v1.4.6(通用版) ### v1.4.6(通用版)
@@ -72,6 +94,22 @@
adb install my-tv.apk adb install my-tv.apk
``` ```
## TODO
* 音量不同
* 大湾区卫视、广东4k超高清、广东珠江
* CETV教育频道
* CHC高清三个电影频道
* 地方频道
* 收藏夹
* 自定义源
* 凤凰卫视、凤凰资讯台
* 海外
* 1.5.0 无法安装,1.5.1 可以安装
* 获取系统时间
* 选中的图标比例能否相差更大
* 自动重连
## 赞赏 ## 赞赏
![image](./screenshots/appreciate.jpeg) ![image](./screenshots/appreciate.jpeg)
+2 -2
View File
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace 'com.lizongying.mytv' namespace 'com.lizongying.mytv'
compileSdk 33 compileSdk 34
viewBinding { viewBinding {
enabled = true enabled = true
@@ -90,7 +90,7 @@ static def VersionName() {
} }
dependencies { dependencies {
def media3_version = "1.1.1" def media3_version = "1.2.1"
implementation "androidx.media3:media3-ui:$media3_version" implementation "androidx.media3:media3-ui:$media3_version"
+5 -3
View File
@@ -9,17 +9,19 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:largeHeap="true"
android:icon="@drawable/logo" android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:banner="@drawable/banner"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:networkSecurityConfig="@xml/network"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.MyTV"> android:theme="@style/Theme.MyTV">
<activity <activity
android:keepScreenOn="true" android:keepScreenOn="true"
android:name=".MainActivity" android:name=".MainActivity"
android:banner="@drawable/banner"
android:exported="true" android:exported="true"
android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:screenOrientation="landscape"> android:screenOrientation="landscape">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,6 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.graphics.Color import android.graphics.Color
import android.util.Log
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -65,23 +64,31 @@ class CardAdapter(private val owner: LifecycleOwner, private var tvListViewModel
cardView.titleText = tvViewModel.title.value cardView.titleText = tvViewModel.title.value
cardView.tag = tvViewModel.videoUrl.value cardView.tag = tvViewModel.videoUrl.value
if (tvViewModel.logo.value != null) { when (tvViewModel.title.value) {
if (tvViewModel.title.value == "CCTV8K 超高清") { "CCTV8K 超高清" -> Glide.with(viewHolder.view.context)
Glide.with(viewHolder.view.context) .load(R.drawable.cctv8k)
.load(R.drawable.cctv8k) .centerInside()
.centerInside() .into(cardView.mainImageView)
.into(cardView.mainImageView)
} else {
Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.centerInside()
.into(cardView.mainImageView)
}
cardView.mainImageView.setBackgroundColor(Color.WHITE) "天津卫视" -> Glide.with(viewHolder.view.context)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE) .load(R.drawable.tianjin)
.centerInside()
.into(cardView.mainImageView)
"新疆卫视" -> Glide.with(viewHolder.view.context)
.load(R.drawable.xinjiang)
.centerInside()
.into(cardView.mainImageView)
else -> Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.centerInside()
.into(cardView.mainImageView)
} }
cardView.mainImageView.setBackgroundColor(Color.WHITE)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
tvViewModel.program.observe(owner) { _ -> tvViewModel.program.observe(owner) { _ ->
val program = tvViewModel.getProgramOne() val program = tvViewModel.getProgramOne()
if (program != null) { if (program != null) {
@@ -31,23 +31,31 @@ class CardPresenter(
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
cardView.tag = tvViewModel.videoUrl.value cardView.tag = tvViewModel.videoUrl.value
if (tvViewModel.logo.value != null) { when (tvViewModel.title.value) {
if (tvViewModel.title.value == "CCTV8K 超高清") { "CCTV8K 超高清" -> Glide.with(viewHolder.view.context)
Glide.with(viewHolder.view.context) .load(R.drawable.cctv8k)
.load(R.drawable.cctv8k) .centerInside()
.centerInside() .into(cardView.mainImageView)
.into(cardView.mainImageView)
} else {
Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.centerInside()
.into(cardView.mainImageView)
}
cardView.setBackgroundColor(Color.WHITE) "天津卫视" -> Glide.with(viewHolder.view.context)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE) .load(R.drawable.tianjin)
.centerInside()
.into(cardView.mainImageView)
"新疆卫视" -> Glide.with(viewHolder.view.context)
.load(R.drawable.xinjiang)
.centerInside()
.into(cardView.mainImageView)
else -> Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.centerInside()
.into(cardView.mainImageView)
} }
cardView.setBackgroundColor(Color.WHITE)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
tvViewModel.program.observe(owner) { _ -> tvViewModel.program.observe(owner) { _ ->
val program = tvViewModel.getProgramOne() val program = tvViewModel.getProgramOne()
if (program != null) { if (program != null) {
@@ -30,7 +30,7 @@ class ChannelFragment : Fragment() {
fun show(tvViewModel: TVViewModel) { fun show(tvViewModel: TVViewModel) {
handler.removeCallbacks(hideRunnable) handler.removeCallbacks(hideRunnable)
handler.removeCallbacks(playRunnable) handler.removeCallbacks(playRunnable)
binding.channelContent.text = tvViewModel.id.value.toString() binding.channelContent.text = (tvViewModel.id.value?.plus(1)).toString()
view?.visibility = View.VISIBLE view?.visibility = View.VISIBLE
handler.postDelayed(hideRunnable, delay) handler.postDelayed(hideRunnable, delay)
} }
@@ -48,6 +48,13 @@ class ChannelFragment : Fragment() {
} }
} }
override fun onResume() {
super.onResume()
if (view?.visibility == View.VISIBLE) {
handler.postDelayed(hideRunnable, delay)
}
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
handler.removeCallbacks(hideRunnable) handler.removeCallbacks(hideRunnable)
@@ -60,7 +67,7 @@ class ChannelFragment : Fragment() {
} }
private val playRunnable = Runnable { private val playRunnable = Runnable {
(activity as MainActivity).play(channel) (activity as MainActivity).play(channel - 1)
binding.channelContent.text = "" binding.channelContent.text = ""
view?.visibility = View.GONE view?.visibility = View.GONE
} }
@@ -2,7 +2,6 @@ package com.lizongying.mytv
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -30,12 +29,21 @@ class InfoFragment : Fragment() {
fun show(tvViewModel: TVViewModel) { fun show(tvViewModel: TVViewModel) {
binding.textView.text = tvViewModel.title.value binding.textView.text = tvViewModel.title.value
if (tvViewModel.title.value == "CCTV8K 超高清") {
Glide.with(this) when (tvViewModel.title.value) {
"CCTV8K 超高清" -> Glide.with(this)
.load(R.drawable.cctv8k) .load(R.drawable.cctv8k)
.into(binding.infoLogo) .into(binding.infoLogo)
} else {
Glide.with(this) "天津卫视" -> Glide.with(this)
.load(R.drawable.tianjin)
.into(binding.infoLogo)
"新疆卫视" -> Glide.with(this)
.load(R.drawable.xinjiang)
.into(binding.infoLogo)
else -> Glide.with(this)
.load(tvViewModel.logo.value) .load(tvViewModel.logo.value)
.into(binding.infoLogo) .into(binding.infoLogo)
} }
@@ -35,16 +35,17 @@ class MainActivity : FragmentActivity() {
private lateinit var gestureDetector: GestureDetector private lateinit var gestureDetector: GestureDetector
private val handler = Handler() private val handler = Handler()
private val delay: Long = 4000 private val delayHideMain: Long = 5000
private val delayHideHelp: Long = 10000 private val delayHideSetting: Long = 10000
private lateinit var sharedPref: SharedPreferences lateinit var sharedPref: SharedPreferences
private var channelReversal = false private var channelReversal = false
private var channelNum = true private var channelNum = true
private var versionName = "" private var versionName = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@@ -60,7 +61,6 @@ class MainActivity : FragmentActivity() {
.add(R.id.main_browse_fragment, mainFragment) .add(R.id.main_browse_fragment, mainFragment)
.hide(mainFragment) .hide(mainFragment)
.commit() .commit()
mainFragment.view?.requestFocus()
} }
gestureDetector = GestureDetector(this, GestureListener()) gestureDetector = GestureDetector(this, GestureListener())
@@ -133,11 +133,11 @@ class MainActivity : FragmentActivity() {
} }
fun keepRunnable() { fun keepRunnable() {
handler.removeCallbacks(hideRunnable) handler.removeCallbacks(hideMain)
handler.postDelayed(hideRunnable, delay) handler.postDelayed(hideMain, delayHideMain)
} }
private val hideRunnable = Runnable { private val hideMain = Runnable {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
supportFragmentManager.beginTransaction().hide(mainFragment).commit() supportFragmentManager.beginTransaction().hide(mainFragment).commit()
} }
@@ -175,7 +175,7 @@ class MainActivity : FragmentActivity() {
} }
override fun onFling( override fun onFling(
e1: MotionEvent, e1: MotionEvent?,
e2: MotionEvent, e2: MotionEvent,
velocityX: Float, velocityX: Float,
velocityY: Float velocityY: Float
@@ -223,7 +223,7 @@ class MainActivity : FragmentActivity() {
this.channelNum = channelNum this.channelNum = channelNum
} }
private fun showHelp() { private fun showSetting() {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
return return
} }
@@ -231,15 +231,15 @@ class MainActivity : FragmentActivity() {
Log.i(TAG, "settingFragment ${settingFragment.isVisible}") Log.i(TAG, "settingFragment ${settingFragment.isVisible}")
if (!settingFragment.isVisible) { if (!settingFragment.isVisible) {
settingFragment.show(supportFragmentManager, "setting") settingFragment.show(supportFragmentManager, "setting")
handler.removeCallbacks(hideHelp) handler.removeCallbacks(hideSetting)
handler.postDelayed(hideHelp, delayHideHelp) handler.postDelayed(hideSetting, delayHideSetting)
} else { } else {
handler.removeCallbacks(hideHelp) handler.removeCallbacks(hideSetting)
settingFragment.dismiss() settingFragment.dismiss()
} }
} }
private val hideHelp = Runnable { private val hideSetting = Runnable {
if (settingFragment.isVisible) { if (settingFragment.isVisible) {
settingFragment.dismiss() settingFragment.dismiss()
} }
@@ -360,27 +360,27 @@ class MainActivity : FragmentActivity() {
} }
KeyEvent.KEYCODE_BOOKMARK -> { KeyEvent.KEYCODE_BOOKMARK -> {
showHelp() showSetting()
return true return true
} }
KeyEvent.KEYCODE_UNKNOWN -> { KeyEvent.KEYCODE_UNKNOWN -> {
showHelp() showSetting()
return true return true
} }
KeyEvent.KEYCODE_HELP -> { KeyEvent.KEYCODE_HELP -> {
showHelp() showSetting()
return true return true
} }
KeyEvent.KEYCODE_SETTINGS -> { KeyEvent.KEYCODE_SETTINGS -> {
showHelp() showSetting()
return true return true
} }
KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_MENU -> {
showHelp() showSetting()
return true return true
} }
@@ -478,7 +478,7 @@ class MainActivity : FragmentActivity() {
private fun hashSignature(signature: Signature): String { private fun hashSignature(signature: Signature): String {
return try { return try {
val md = MessageDigest.getInstance("SHA-256") val md = MessageDigest.getInstance("MD5")
md.update(signature.toByteArray()) md.update(signature.toByteArray())
val digest = md.digest() val digest = md.digest()
digest.let { it -> it.joinToString("") { "%02x".format(it) } } digest.let { it -> it.joinToString("") { "%02x".format(it) } }
@@ -488,6 +488,25 @@ class MainActivity : FragmentActivity() {
} }
} }
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
if (!mainFragment.isHidden){
handler.postDelayed(hideMain, delayHideMain)
}
}
override fun onPause() {
Log.i(TAG, "onPause")
super.onPause()
handler.removeCallbacks(hideMain)
}
companion object { companion object {
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
private const val CHANNEL_REVERSAL = "channel_reversal" private const val CHANNEL_REVERSAL = "channel_reversal"
@@ -1,6 +1,5 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@@ -36,7 +35,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private var sharedPref: SharedPreferences? = null private lateinit var sharedPref: SharedPreferences
private var lastVideoUrl = "" private var lastVideoUrl = ""
@@ -57,7 +56,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
activity?.let { request.initYSP(it) } activity?.let { request.initYSP(it) }
sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) sharedPref = (activity as? MainActivity)?.sharedPref!!
view?.post { view?.post {
val content = binding.content val content = binding.content
@@ -98,13 +97,13 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
mUpdateProgramRunnable = UpdateProgramRunnable() mUpdateProgramRunnable = UpdateProgramRunnable()
handler.post(mUpdateProgramRunnable) handler.post(mUpdateProgramRunnable)
itemPosition = sharedPref?.getInt(POSITION, 0)!! itemPosition = sharedPref.getInt(POSITION, 0)
if (itemPosition >= tvListViewModel.size()) { if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0 itemPosition = 0
} }
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
setPosition() setPosition()
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel -> tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ -> tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.errInfo.value != null if (tvViewModel.errInfo.value != null
&& tvViewModel.id.value == itemPosition && tvViewModel.id.value == itemPosition
@@ -129,7 +128,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
val title = tvViewModel.title.value val title = tvViewModel.title.value
Log.i(TAG, "switch $title") Log.i(TAG, "switch $title")
if (tvViewModel.pid.value != "") { if (tvViewModel.pid.value != "") {
Log.i(TAG, "request $title ${tvViewModel.pid.value}") Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { request.fetchData(it) } tvViewModel.let { request.fetchData(it) }
} }
@@ -223,7 +222,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
fun play(itemPosition: Int) { fun play(itemPosition: Int) {
view?.post { view?.post {
if (itemPosition < tvListViewModel.size()) { if (itemPosition > -1 && itemPosition < tvListViewModel.size()) {
this.itemPosition = itemPosition this.itemPosition = itemPosition
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed()
@@ -262,7 +261,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) request.fetchProgram(tvViewModel)
} else { } else {
if (timestamp - tvViewModel.program.value!!.last().et < 600) { if (tvViewModel.program.value!!.last().et - timestamp < 600) {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) request.fetchProgram(tvViewModel)
} }
@@ -272,7 +271,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
inner class UpdateProgramRunnable : Runnable { inner class UpdateProgramRunnable : Runnable {
override fun run() { override fun run() {
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null } tvListViewModel.tvListViewModel.value?.filter { it.programId.value != null && it.programId.value != "" }
?.forEach { tvViewModel -> ?.forEach { tvViewModel ->
updateProgram( updateProgram(
tvViewModel tvViewModel
@@ -282,14 +281,19 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
} }
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onStop() { override fun onStop() {
Log.i(TAG, "onStop") Log.i(TAG, "onStop")
super.onStop() super.onStop()
with(sharedPref!!.edit()) { with(sharedPref.edit()) {
putInt(POSITION, itemPosition) putInt(POSITION, itemPosition)
apply() apply()
} }
Log.i(TAG, "POSITION saved") Log.i(TAG, "$POSITION saved")
} }
override fun onDestroy() { override fun onDestroy() {
@@ -304,11 +308,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
_binding = null _binding = null
} }
override fun onResume() {
super.onResume()
view?.post { view?.requestFocus() }
}
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
private const val POSITION = "position" private const val POSITION = "position"
@@ -74,23 +74,31 @@ class PlayerFragment : Fragment() {
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun play(tvViewModel: TVViewModel) { fun play(tvViewModel: TVViewModel) {
this.tvViewModel = tvViewModel this.tvViewModel = tvViewModel
val videoUrlCurrent = tvViewModel.getVideoUrlCurrent()
playerView?.player?.run { playerView?.player?.run {
setMediaItem(MediaItem.fromUri(videoUrlCurrent)) setMediaItem(MediaItem.fromUri(tvViewModel.getVideoUrlCurrent()))
prepare() prepare()
} }
} }
override fun onStart() { override fun onStart() {
Log.i(TAG, "onStart")
super.onStart() super.onStart()
if (playerView != null) { if (playerView != null && playerView!!.player?.isPlaying == false) {
playerView!!.player?.play() Log.i(TAG, "replay")
playerView!!.player?.prepare()
} else {
Log.i(TAG, "playing")
} }
} }
override fun onStop() { override fun onResume() {
super.onStop() Log.i(TAG, "onResume")
if (playerView != null) { super.onResume()
}
override fun onPause() {
super.onPause()
if (playerView != null && playerView!!.player?.isPlaying == true) {
playerView!!.player?.stop() playerView!!.player?.stop()
} }
} }
+156 -65
View File
@@ -32,10 +32,16 @@ class Request {
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
private var ysp: YSP? = null private var ysp: YSP? = null
private var token = ""
// TODO onDestroy // TODO onDestroy
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private lateinit var myRunnable: MyRunnable private lateinit var btraceRunnable: BtraceRunnable
private var tokenRunnable: TokenRunnable = TokenRunnable()
private val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
private val input =
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,ANGLEMetalRenderer:AppleM1Pro,UnspecifiedVersion)"}""".toByteArray()
private var mapping = mapOf( private var mapping = mapOf(
"CCTV4K" to "CCTV4K 超高清", "CCTV4K" to "CCTV4K 超高清",
@@ -81,8 +87,14 @@ class Request {
"四川卫视" to "四川卫视", "四川卫视" to "四川卫视",
"福建东南卫视" to "东南卫视", "福建东南卫视" to "东南卫视",
"海南卫视" to "海南卫视", "海南卫视" to "海南卫视",
"天津卫视" to "天津卫视",
"新疆卫视" to "新疆卫视",
) )
init {
handler.post(tokenRunnable)
}
fun initYSP(context: Context) { fun initYSP(context: Context) {
ysp = YSP(context) ysp = YSP(context)
} }
@@ -91,8 +103,8 @@ class Request {
fun fetchVideo(tvModel: TVViewModel, cookie: String) { fun fetchVideo(tvModel: TVViewModel, cookie: String) {
call?.cancel() call?.cancel()
if (::myRunnable.isInitialized) { if (::btraceRunnable.isInitialized) {
handler.removeCallbacks(myRunnable) handler.removeCallbacks(btraceRunnable)
} }
val title = tvModel.title.value val title = tvModel.title.value
@@ -106,6 +118,7 @@ class Request {
override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) { override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) {
if (response.isSuccessful) { if (response.isSuccessful) {
val liveInfo = response.body() val liveInfo = response.body()
if (liveInfo?.data?.playurl != null) { if (liveInfo?.data?.playurl != null) {
val chanll = liveInfo.data.chanll val chanll = liveInfo.data.chanll
val decodedBytes = Base64.decode( val decodedBytes = Base64.decode(
@@ -113,7 +126,6 @@ class Request {
Base64.DEFAULT Base64.DEFAULT
) )
val decodedString = String(decodedBytes) val decodedString = String(decodedBytes)
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val matchResult = regex.find(decodedString) val matchResult = regex.find(decodedString)
if (matchResult != null) { if (matchResult != null) {
val (key, iv) = matchResult.destructured val (key, iv) = matchResult.destructured
@@ -122,18 +134,29 @@ class Request {
val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES( val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0), keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
ivBytes ivBytes
).uppercase() ).uppercase() + liveInfo.data.extended_param
Log.i(TAG, "$title url $url") Log.d(TAG, "$title url $url")
tvModel.addVideoUrl(url) tvModel.addVideoUrl(url)
tvModel.allReady() tvModel.allReady()
tvModel.retryTimes = 0 tvModel.retryTimes = 0
myRunnable = MyRunnable(tvModel) btraceRunnable = BtraceRunnable(tvModel)
handler.post(myRunnable) handler.post(btraceRunnable)
} else { } else {
Log.e(TAG, "$title key error") Log.e(TAG, "$title key error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
fetchVideo(tvModel, cookie) if (tvModel.getTV().needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} else { } else {
@@ -144,7 +167,18 @@ class Request {
Log.e(TAG, "$title url error $request $liveInfo") Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
fetchVideo(tvModel, cookie) if (tvModel.getTV().needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} }
@@ -152,60 +186,126 @@ class Request {
Log.e(TAG, "$title status error") Log.e(TAG, "$title status error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
fetchVideo(tvModel, cookie) if (tvModel.getTV().needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} }
override fun onFailure(call: Call<LiveInfo>, t: Throwable) { override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
Log.e(TAG, "$title request error") Log.e(TAG, "$title request error $t")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
fetchVideo(tvModel, cookie) if (tvModel.getTV().needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
}) })
} }
fun fetchVideo(tvModel: TVViewModel) { fun fetchVideo(tvModel: TVViewModel) {
yspTokenService.getInfo() if (token == "") {
.enqueue(object : Callback<Info> { yspTokenService.getInfo()
override fun onResponse(call: Call<Info>, response: Response<Info>) { .enqueue(object : Callback<Info> {
if (response.isSuccessful) { override fun onResponse(call: Call<Info>, response: Response<Info>) {
val token = response.body()?.data?.token if (response.isSuccessful) {
Log.i(TAG, "info success $token") token = response.body()?.data?.token!!
val cookie = Log.i(TAG, "info success $token")
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" val cookie =
fetchVideo(tvModel, cookie) "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
} else { fetchVideo(tvModel, cookie)
Log.e(TAG, "info status error") } else {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { Log.e(TAG, "info status error")
tvModel.retryTimes++ if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) {
fetchVideo(tvModel) tvModel.tokenRetryTimes++
fetchVideo(tvModel)
} else {
if (!tvModel.getTV().mustToken) {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109"
fetchVideo(tvModel, cookie)
}
}
} }
} }
}
override fun onFailure(call: Call<Info>, t: Throwable) { override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "info request error $t") Log.e(TAG, "info request error $t")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) {
tvModel.retryTimes++ tvModel.tokenRetryTimes++
fetchVideo(tvModel) fetchVideo(tvModel)
} else {
if (!tvModel.getTV().mustToken) {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109"
fetchVideo(tvModel, cookie)
}
}
} }
} })
})
}
fun fetchData(tvModel: TVViewModel) {
if (tvModel.needToken) {
fetchVideo(tvModel)
} else { } else {
val cookie = "vplatform=109" val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109;yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
} }
inner class MyRunnable(private val tvModel: TVViewModel) : Runnable { fun fetchData(tvModel: TVViewModel) {
if (tvModel.getTV().needToken) {
fetchVideo(tvModel)
} else {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; yspappid=519748109"
fetchVideo(tvModel, cookie)
}
}
inner class TokenRunnable : Runnable {
override fun run() {
fetchToken()
handler.postDelayed(this, 600000)
}
}
fun fetchToken() {
yspTokenService.getInfo()
.enqueue(object : Callback<Info> {
override fun onResponse(call: Call<Info>, response: Response<Info>) {
if (response.isSuccessful) {
token = response.body()?.data?.token!!
Log.i(TAG, "info success $token")
} else {
Log.e(TAG, "token status error")
}
}
override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "token request error $t")
}
})
}
inner class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() { override fun run() {
fetchBtrace(tvModel) fetchBtrace(tvModel)
handler.postDelayed(this, 60000) handler.postDelayed(this, 60000)
@@ -255,29 +355,21 @@ class Request {
) { ) {
if (response.isSuccessful) { if (response.isSuccessful) {
val body = response.body() val body = response.body()
if (body?.data?.feedModuleListCount == 1) { if (body?.data?.feedModuleListCount == 1) {
for (item in body.data?.feedModuleListList!![0]?.dataTvChannelListList!!) { for (item in body.data?.feedModuleListList!![0]?.dataTvChannelListList!!) {
if (item.isVip && !item.isLimitedFree) { Log.d(
continue
}
Log.i(
TAG, TAG,
"${item.channelName},${item.pid},${item.streamId}" "${item.channelName},${item.pid},${item.streamId}"
) )
var channelType = "央视频道"
if (item?.channelType === "weishi") { for ((_, v) in TVList.list) {
channelType = "地方频道" for (v2 in v) {
} Log.i(TAG, "$v2")
if (!mapping.containsKey(item.channelName)) { if (v2.title == item.channelName || v2.alias == item.channelName) {
continue v2.pid = item.pid
} v2.sid = item.streamId
val tv = }
TVList.list[channelType]?.find { it.title == mapping[item.channelName] } }
if (tv != null) {
tv.logo = item.tvLogo
tv.pid = item.pid
tv.sid = item.streamId
} }
} }
} }
@@ -302,8 +394,10 @@ class Request {
val program = response.body() val program = response.body()
if (program != null) { if (program != null) {
tvViewModel.addProgram(program.dataListList) tvViewModel.addProgram(program.dataListList)
Log.i(TAG, "$title program ${program.dataListList.size}") Log.d(TAG, "$title program ${program.dataListList.size}")
} }
} else {
Log.w(TAG, "$title program error")
} }
} }
@@ -314,15 +408,12 @@ class Request {
} }
private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String { private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String {
val plaintext =
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,AppleM1Pro,OpenGL4.1)"}"""
return try { return try {
val keySpec = SecretKeySpec(key, "DESede") val keySpec = SecretKeySpec(key, "DESede")
val ivSpec = IvParameterSpec(iv) val ivSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding") val cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encryptedBytes = cipher.doFinal(plaintext.toByteArray()) return cipher.doFinal(input).let { it -> it.joinToString("") { "%02x".format(it) } }
return encryptedBytes.let { it -> it.joinToString("") { "%02x".format(it) } }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
"" ""
+6 -1
View File
@@ -5,13 +5,18 @@ import java.io.Serializable
data class TV( data class TV(
var id: Int = 0, var id: Int = 0,
var title: String, var title: String,
var alias: String = "",
var videoUrl: List<String>, var videoUrl: List<String>,
var videoIndex: Int = 0, var videoIndex: Int = 0,
var channel: String = "",
var logo: String = "", var logo: String = "",
var pid: String = "", var pid: String = "",
var sid: String = "", var sid: String = "",
var programId: String = "", var programId: String = "",
) : Serializable { var needToken: Boolean = false,
var mustToken: Boolean = false,
) : Serializable {
override fun toString(): String { override fun toString(): String {
return "TV{" + return "TV{" +
File diff suppressed because it is too large Load Diff
+51 -8
View File
@@ -1,10 +1,12 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.util.TypedValue import android.util.TypedValue
import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.*
import java.util.Locale import java.io.IOException
object Utils { object Utils {
fun getDateFormat(format: String): String { fun getDateFormat(format: String): String {
@@ -17,17 +19,58 @@ object Utils {
fun dpToPx(dp: Float): Int { fun dpToPx(dp: Float): Int {
return TypedValue.applyDimension( return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics
dp,
Resources.getSystem().displayMetrics
).toInt() ).toInt()
} }
fun dpToPx(dp: Int): Int { fun dpToPx(dp: Int): Int {
return TypedValue.applyDimension( return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics
dp.toFloat(),
Resources.getSystem().displayMetrics
).toInt() ).toInt()
} }
/**
* 获取可读写的目录
*
* @param context 应用环境信息
*
* @return 可读写的目录
*
*/
fun getAppDirectory(context: Context): File {
return context.filesDir
}
/**
* 更新channels.json
*
* @param context 应用环境信息
*
* @return 无
*
* @throws IOException 网络请求失败
*/
fun updateChannel(context: Context) {
val client = okhttp3.OkHttpClient()
val request = okhttp3.Request.Builder().url(getServerUrl(context)).build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
val body = response.body()
//覆盖channels.json
val file = File(getAppDirectory(context), "channels.json")
if (!file.exists()) {
file.createNewFile()
}
file.writeText(body!!.string())
}
}
/**
* 从res/values/server.xml获取服务器地址
* @param context 应用环境信息
* @return 服务器地址
*/
private fun getServerUrl(context: Context): String {
return context.resources.getString(R.string.server_url)
}
} }
@@ -13,6 +13,7 @@ data class LiveInfo(
) )
data class LiveInfoData( data class LiveInfoData(
val extended_param: String,
val chanll: String, val chanll: String,
val playurl: String, val playurl: String,
val errinfo: String, val errinfo: String,
@@ -51,14 +51,14 @@ class YSP(var context: Context) {
private var signature = "" private var signature = ""
private var encryptor: Encryptor? = null private var encryptor: Encryptor? = null
private var sharedPref: SharedPreferences? = null private lateinit var sharedPref: SharedPreferences
init { init {
if (context is MainActivity) { if (context is MainActivity) {
encryptor = Encryptor() encryptor = Encryptor()
encryptor!!.init(context) encryptor!!.init(context)
sharedPref = (context as MainActivity).getPreferences(Context.MODE_PRIVATE) sharedPref = (context as MainActivity).sharedPref
} }
guid = getGuid() guid = getGuid()
@@ -77,15 +77,10 @@ class YSP(var context: Context) {
timeStr = getTimeStr() timeStr = getTimeStr()
// guid = "lq3oqitm_1e15dnzgjnb"
// randStr = "BfcCPQp8Hq"
// timeStr = "1702166501"
cKey = cKey =
encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform) encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform)
signature = getSignature() signature = getSignature()
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"}""" 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"}"""
// return """{"cnlid":"2000203803","livepid":"600001801","stream":"2","guid":"lq1y36mb_ccfwmja9zan","cKey":"--01A9F5E89BB86A0C61F4025BEDE15309B6913A79FD1AF1EE7F5EC9C7605F377D1D2281488385C32DEB9E7D0DD3559CB700BD7AF44DD5C9DE0AE14D94B8214027B5D664C108AEE23532348DCC61B86F7C8FBB6CF14D588E6093A25E97DF6D66F4882AB28F17016472DD43D45EF076B7F505176A5E8DEDF2662E5F9AB12B69CB20BCE1579BE724091F3AF6826AE34B713906F3FE139C3783F80EECBD08416DC525E1","adjust":1,"sphttps":"1","platform":"5910204","cmd":"2","encryptVer":"8.1","dtype":"1","devid":"devid","otype":"ojson","appVer":"V1.0.0","app_version":"V1.0.0","rand_str":"IOS2soOw44","channel":"ysp_tx","defn":"fhd","signature":"6ca945f651817de8c6e6910457ceafd6"}"""
} }
private fun getTimeStr(): String { private fun getTimeStr(): String {
@@ -99,10 +94,10 @@ class YSP(var context: Context) {
} }
fun getGuid(): String { fun getGuid(): String {
var guid = sharedPref?.getString("guid", "") var guid = sharedPref.getString("guid", "")
if (guid == null || guid.length < 18) { if (guid == null || guid.length < 18) {
guid = generateGuid() guid = generateGuid()
with(sharedPref!!.edit()) { with(sharedPref.edit()) {
putString("guid", guid) putString("guid", guid)
apply() apply()
} }
@@ -112,7 +107,7 @@ class YSP(var context: Context) {
private fun newGuid(): String { private fun newGuid(): String {
guid = generateGuid() guid = generateGuid()
with(sharedPref!!.edit()) { with(sharedPref.edit()) {
putString("guid", guid) putString("guid", guid)
apply() apply()
} }
@@ -132,7 +127,6 @@ class YSP(var context: Context) {
private fun getSignature(): String { private fun getSignature(): String {
val e = val e =
"adjust=${adjust}&appVer=${appVer}&app_version=$appVersion&cKey=$cKey&channel=$channel&cmd=$cmd&cnlid=$cnlid&defn=${defn}&devid=${devid}&dtype=${dtype}&encryptVer=${encryptVer}&guid=${guid}&livepid=${livepid}&otype=${otype}&platform=${platform}&rand_str=${randStr}&sphttps=${sphttps}&stream=${stream}".toByteArray() "adjust=${adjust}&appVer=${appVer}&app_version=$appVersion&cKey=$cKey&channel=$channel&cmd=$cmd&cnlid=$cnlid&defn=${defn}&devid=${devid}&dtype=${dtype}&encryptVer=${encryptVer}&guid=${guid}&livepid=${livepid}&otype=${otype}&platform=${platform}&rand_str=${randStr}&sphttps=${sphttps}&stream=${stream}".toByteArray()
val hashedData = encryptor?.hash(e) ?: return "" val hashedData = encryptor?.hash(e) ?: return ""
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
} }
@@ -8,7 +8,9 @@ class TVListViewModel : ViewModel() {
var maxNum = mutableListOf<Int>() var maxNum = mutableListOf<Int>()
private val tvListViewModel = MutableLiveData<MutableList<TVViewModel>>() private val _tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
val tvListViewModel: LiveData<MutableList<TVViewModel>>
get() = _tvListViewModel
private val _itemPosition = MutableLiveData<Int>() private val _itemPosition = MutableLiveData<Int>()
val itemPosition: LiveData<Int> val itemPosition: LiveData<Int>
@@ -18,22 +20,20 @@ class TVListViewModel : ViewModel() {
val itemPositionCurrent: LiveData<Int> val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent get() = _itemPositionCurrent
fun getTVListViewModel(): MutableLiveData<MutableList<TVViewModel>> {
return tvListViewModel
}
fun addTVViewModel(tvViewModel: TVViewModel) { fun addTVViewModel(tvViewModel: TVViewModel) {
val currentTVModelList = tvListViewModel.value ?: mutableListOf() if (_tvListViewModel.value == null) {
currentTVModelList.add(tvViewModel) _tvListViewModel.value = mutableListOf(tvViewModel)
tvListViewModel.value = currentTVModelList } else {
_tvListViewModel.value?.add(tvViewModel)
}
} }
fun getTVViewModel(id: Int): TVViewModel? { fun getTVViewModel(id: Int): TVViewModel? {
return tvListViewModel.value?.get(id) return _tvListViewModel.value?.get(id)
} }
fun getTVViewModelCurrent(): TVViewModel? { fun getTVViewModelCurrent(): TVViewModel? {
return _itemPositionCurrent.value?.let { tvListViewModel.value?.get(it) } return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) }
} }
fun setItemPosition(position: Int) { fun setItemPosition(position: Int) {
@@ -46,6 +46,9 @@ class TVListViewModel : ViewModel() {
} }
fun size(): Int { fun size(): Int {
return tvListViewModel.value!!.size if (_tvListViewModel.value == null) {
return 0
}
return _tvListViewModel.value!!.size
} }
} }
@@ -19,8 +19,10 @@ class TVViewModel(private var tv: TV) : ViewModel() {
private var rowPosition: Int = 0 private var rowPosition: Int = 0
private var itemPosition: Int = 0 private var itemPosition: Int = 0
var retryTimes: Int = 0 var retryTimes = 0
var retryMaxTimes: Int = 8 var tokenRetryTimes = 0
var retryMaxTimes = 8
var tokenRetryMaxTimes = 2
var programUpdateTime: Long = 0 var programUpdateTime: Long = 0
private val _errInfo = MutableLiveData<String>() private val _errInfo = MutableLiveData<String>()
@@ -75,26 +77,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
var seq = 0 var seq = 0
var needToken = false
private val channelsNeedToken = arrayOf(
"CCTV3 综艺",
"CCTV6 电影",
"CCTV8 电视剧",
"风云剧场",
"第一剧场",
"怀旧剧场",
"世界地理",
"风云音乐",
"兵器科技",
"风云足球",
"高尔夫网球",
"女性时尚",
"央视文化精品",
"央视台球",
"电视指南",
"卫生健康",
)
fun addVideoUrl(url: String) { fun addVideoUrl(url: String) {
if (_videoUrl.value?.isNotEmpty() == true) { if (_videoUrl.value?.isNotEmpty() == true) {
@@ -146,12 +128,9 @@ class TVViewModel(private var tv: TV) : ViewModel() {
_pid.value = tv.pid _pid.value = tv.pid
_sid.value = tv.sid _sid.value = tv.sid
_program.value = mutableListOf() _program.value = mutableListOf()
if (tv.title in channelsNeedToken) {
needToken = true
}
} }
fun getRowPosition(): Int { fun getRowPosition(): Int {
return rowPosition return rowPosition
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+954
View File
@@ -0,0 +1,954 @@
[
{
"id": 0,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
"pid": "600001859",
"sid": "2000210103",
"programId": "600001859",
"needToken": false,
"mustToken": false,
"title": "CCTV1 综合",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8"
]
},
{
"id": 1,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
"pid": "600001800",
"sid": "2000203603",
"programId": "600001800",
"needToken": true,
"mustToken": false,
"title": "CCTV2 财经",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8"
]
},
{
"id": 2,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp",
"pid": "600001801",
"sid": "2000203803",
"programId": "600001801",
"needToken": true,
"mustToken": true,
"title": "CCTV3 综艺",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8"
]
},
{
"id": 3,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
"pid": "600001814",
"sid": "2000204803",
"programId": "600001814",
"needToken": false,
"mustToken": false,
"title": "CCTV4 中文国际",
"videoUrl": [
"http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8"
]
},
{
"id": 4,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
"pid": "600001818",
"sid": "2000205103",
"programId": "600001818",
"needToken": true,
"mustToken": false,
"title": "CCTV5 体育",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226395/index.m3u8"
]
},
{
"id": 5,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
"pid": "600001802",
"sid": "2013693901",
"programId": "600001802",
"needToken": true,
"mustToken": true,
"title": "CCTV6 电影",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226393/index.m3u8"
]
},
{
"id": 6,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
"pid": "600004092",
"sid": "2000510003",
"programId": "600004092",
"needToken": true,
"mustToken": false,
"title": "CCTV7 国防军事",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226192/index.m3u8"
]
},
{
"id": 7,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
"pid": "600001803",
"sid": "2000203903",
"programId": "600001803",
"needToken": true,
"mustToken": true,
"title": "CCTV8 电视剧",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226391/index.m3u8"
]
},
{
"id": 8,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
"pid": "600004078",
"sid": "2000499403",
"programId": "600004078",
"needToken": true,
"mustToken": false,
"title": "CCTV9 记录",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8"
]
},
{
"id": 9,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
"pid": "600001805",
"sid": "2000203503",
"programId": "600001805",
"needToken": true,
"mustToken": false,
"title": "CCTV10 科教",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226189/index.m3u8"
]
},
{
"id": 10,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
"pid": "600001806",
"sid": "2000204103",
"programId": "600001806",
"needToken": true,
"mustToken": false,
"title": "CCTV11 戏曲",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226240/index.m3u8"
]
},
{
"id": 11,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
"pid": "600001807",
"sid": "2000202603",
"programId": "600001807",
"needToken": true,
"mustToken": false,
"title": "CCTV12 社会与法",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226190/index.m3u8"
]
},
{
"id": 12,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
"pid": "600001811",
"sid": "2000204603",
"programId": "600001811",
"needToken": false,
"mustToken": false,
"title": "CCTV13 新闻",
"videoUrl": [
"https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226233/index.m3u8"
]
},
{
"id": 13,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
"pid": "600001809",
"sid": "2000204403",
"programId": "600001809",
"needToken": true,
"mustToken": false,
"title": "CCTV14 少儿",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8"
]
},
{
"id": 14,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
"pid": "600001815",
"sid": "2000205003",
"programId": "600001815",
"needToken": true,
"mustToken": false,
"title": "CCTV15 音乐",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8"
]
},
{
"id": 15,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
"pid": "600098637",
"sid": "2012375003",
"programId": "600098637",
"needToken": true,
"mustToken": false,
"title": "CCTV16 奥林匹克",
"videoUrl": [
"http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8"
]
},
{
"id": 16,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
"pid": "600001810",
"sid": "2000204203",
"programId": "600001810",
"needToken": true,
"mustToken": false,
"title": "CCTV17 农业农村",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8"
]
},
{
"id": 17,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
"pid": "600001817",
"sid": "2000204503",
"programId": "600001817",
"needToken": true,
"mustToken": false,
"title": "CCTV5+ 体育赛事",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226221/index.m3u8"
]
},
{
"id": 18,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp",
"pid": "600002264",
"sid": "2000266303",
"programId": "600002264",
"needToken": true,
"mustToken": false,
"title": "CCTV4K 超高清",
"videoUrl": [
""
]
},
{
"id": 19,
"videoIndex": 0,
"channel": "央视频道",
"logo": "",
"pid": "600156816",
"sid": "2020603421",
"programId": "",
"needToken": false,
"mustToken": false,
"title": "CCTV8K 超高清",
"videoUrl": [
""
]
},
{
"id": 20,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp",
"pid": "600099658",
"sid": "2012513603",
"programId": "600099658",
"needToken": true,
"mustToken": true,
"title": "风云剧场",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8"
]
},
{
"id": 21,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp",
"pid": "600099655",
"sid": "2012514403",
"programId": "600099655",
"needToken": true,
"mustToken": true,
"title": "第一剧场",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8"
]
},
{
"id": 22,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp",
"pid": "600099620",
"sid": "2012511203",
"programId": "600099620",
"needToken": true,
"mustToken": true,
"title": "怀旧剧场",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8"
]
},
{
"id": 23,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/bb3c6c9e145d698137f5bb64a582021a01b51344b929003630eb769ea65832a9.png?imageMogr2/format/webp",
"pid": "600099637",
"sid": "2012513303",
"programId": "600099637",
"needToken": true,
"mustToken": true,
"title": "世界地理",
"videoUrl": [
""
]
},
{
"id": 24,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp",
"pid": "600099660",
"sid": "2012514103",
"programId": "600099660",
"needToken": true,
"mustToken": true,
"title": "风云音乐",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226953/index.m3u8"
]
},
{
"id": 25,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp",
"pid": "600099649",
"sid": "2012513403",
"programId": "600099649",
"needToken": true,
"mustToken": true,
"title": "兵器科技",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8"
]
},
{
"id": 26,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp",
"pid": "600099636",
"sid": "2012514203",
"programId": "600099636",
"needToken": true,
"mustToken": true,
"title": "风云足球",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8"
]
},
{
"id": 27,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp",
"pid": "600099659",
"sid": "2012512503",
"programId": "600099659",
"needToken": true,
"mustToken": true,
"title": "高尔夫网球",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8"
]
},
{
"id": 28,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp",
"pid": "600099650",
"sid": "2012513903",
"programId": "600099650",
"needToken": true,
"mustToken": true,
"title": "女性时尚",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226969/index.m3u8"
]
},
{
"id": 29,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp",
"pid": "600099653",
"sid": "2012513803",
"programId": "600099653",
"needToken": true,
"mustToken": true,
"title": "央视文化精品",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8"
]
},
{
"id": 30,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp",
"pid": "600099652",
"sid": "2012513703",
"programId": "600099652",
"needToken": true,
"mustToken": true,
"title": "央视台球",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226956/index.m3u8"
]
},
{
"id": 31,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp",
"pid": "600099656",
"sid": "2012514003",
"programId": "600099656",
"needToken": true,
"mustToken": true,
"title": "电视指南",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8"
]
},
{
"id": 32,
"videoIndex": 0,
"channel": "央视频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/54a6863656fdfd8f803be193ddf22441c5000a108833889816fd2d8911715ce8.png?imageMogr2/format/webp",
"pid": "600099651",
"sid": "2012513503",
"programId": "600099651",
"needToken": true,
"mustToken": true,
"title": "卫生健康",
"videoUrl": [
""
]
},
{
"id": 33,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
"pid": "600002483",
"sid": "2000292403",
"programId": "600002483",
"needToken": true,
"mustToken": false,
"title": "东方卫视",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226217/index.m3u8"
]
},
{
"id": 34,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
"pid": "600002475",
"sid": "2000296203",
"programId": "600002475",
"needToken": true,
"mustToken": false,
"title": "湖南卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226211/index.m3u8"
]
},
{
"id": 35,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
"pid": "600002508",
"sid": "2000294503",
"programId": "600002508",
"needToken": true,
"mustToken": false,
"title": "湖北卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226194/index.m3u8"
]
},
{
"id": 36,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
"pid": "600002505",
"sid": "2000281303",
"programId": "600002505",
"needToken": true,
"mustToken": false,
"title": "辽宁卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226201/index.m3u8"
]
},
{
"id": 37,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
"pid": "600002521",
"sid": "2000295603",
"programId": "600002521",
"needToken": true,
"mustToken": false,
"title": "江苏卫视",
"videoUrl": [
"http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8"
]
},
{
"id": 38,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
"pid": "600002503",
"sid": "2000294103",
"programId": "600002503",
"needToken": true,
"mustToken": false,
"title": "江西卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226344/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225764/index.m3u8"
]
},
{
"id": 39,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
"pid": "600002513",
"sid": "2000294803",
"programId": "600002513",
"needToken": true,
"mustToken": false,
"title": "山东卫视",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226209/index.m3u8"
]
},
{
"id": 40,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
"pid": "600002485",
"sid": "2000292703",
"programId": "600002485",
"needToken": true,
"mustToken": false,
"title": "广东卫视",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226216/index.m3u8"
]
},
{
"id": 41,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
"pid": "600002509",
"sid": "2000294203",
"programId": "600002509",
"needToken": true,
"mustToken": false,
"title": "广西卫视",
"videoUrl": [
"http://live.gxrb.com.cn/tv/gxtvlive03/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225770/index.m3u8"
]
},
{
"id": 42,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/657651f411de2673d1770d9a78b44c1265704f7468cc41d4be7f51d630768494.png?imageMogr2/format/webp",
"pid": "600002531",
"sid": "2000297803",
"programId": "600002531",
"needToken": true,
"mustToken": false,
"title": "重庆卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226409/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226202/index.m3u8"
]
},
{
"id": 43,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
"pid": "600002525",
"sid": "2000296103",
"programId": "600002525",
"needToken": true,
"mustToken": false,
"title": "河南卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226480/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225767/index.m3u8"
]
},
{
"id": 44,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
"pid": "600002493",
"sid": "2000293403",
"programId": "600002493",
"needToken": true,
"mustToken": false,
"title": "河北卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226406/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225750/index.m3u8"
]
},
{
"id": 45,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
"pid": "600002490",
"sid": "2000293303",
"programId": "600002490",
"needToken": true,
"mustToken": false,
"title": "贵州卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226474/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225793/index.m3u8"
]
},
{
"id": 46,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
"pid": "600002309",
"sid": "2000272103",
"programId": "600002309",
"needToken": true,
"mustToken": false,
"title": "北京卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225728/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226222/index.m3u8"
]
},
{
"id": 47,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
"pid": "600002498",
"sid": "2000293903",
"programId": "600002498",
"needToken": true,
"mustToken": false,
"title": "黑龙江卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226327/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226215/index.m3u8"
]
},
{
"id": 48,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
"pid": "600002520",
"sid": "2000295503",
"programId": "600002520",
"needToken": true,
"mustToken": false,
"title": "浙江卫视",
"videoUrl": [
"http://hw-m-l.cztv.com/channels/lantian/channel01/1080p.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226199/index.m3u8"
]
},
{
"id": 49,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
"pid": "600002532",
"sid": "2000298003",
"programId": "600002532",
"needToken": true,
"mustToken": false,
"title": "安徽卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226203/index.m3u8"
]
},
{
"id": 50,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
"pid": "600002481",
"sid": "2000292203",
"programId": "600002481",
"needToken": true,
"mustToken": false,
"title": "深圳卫视",
"videoUrl": [
"http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8"
]
},
{
"id": 51,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
"pid": "600002516",
"sid": "2000295003",
"programId": "600002516",
"needToken": true,
"mustToken": false,
"title": "四川卫视",
"videoUrl": [
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8",
"http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8"
]
},
{
"id": 52,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
"pid": "600002484",
"sid": "2000292503",
"programId": "600002484",
"needToken": true,
"mustToken": false,
"title": "东南卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8"
]
},
{
"id": 53,
"videoIndex": 0,
"channel": "地方频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
"pid": "600002506",
"sid": "2000291503",
"programId": "600002506",
"needToken": true,
"mustToken": false,
"title": "海南卫视",
"videoUrl": [
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8"
]
},
{
"id": 54,
"videoIndex": 0,
"channel": "地方频道",
"logo": "",
"pid": "600152137",
"sid": "2019927003",
"programId": "",
"needToken": true,
"mustToken": false,
"title": "天津卫视",
"videoUrl": [
""
]
},
{
"id": 55,
"videoIndex": 0,
"channel": "地方频道",
"logo": "",
"pid": "600152138",
"sid": "2019927403",
"programId": "",
"needToken": true,
"mustToken": false,
"title": "新疆卫视",
"videoUrl": [
""
]
},
{
"id": 56,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
"pid": "600014550",
"sid": "2001656803",
"programId": "600014550",
"needToken": false,
"mustToken": false,
"title": "CGTN",
"videoUrl": [
"http://live.cgtn.com/1000/prog_index.m3u8"
]
},
{
"id": 57,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
"pid": "600084704",
"sid": "2010153503",
"programId": "600084704",
"needToken": false,
"mustToken": false,
"title": "CGTN 法语频道",
"videoUrl": [
"https://livefr.cgtn.com/1000f/prog_index.m3u8"
]
},
{
"id": 58,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
"pid": "600084758",
"sid": "2010152603",
"programId": "600084758",
"needToken": false,
"mustToken": false,
"title": "CGTN 俄语频道",
"videoUrl": [
"http://liveru.cgtn.com/1000r/prog_index.m3u8"
]
},
{
"id": 59,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
"pid": "600084782",
"sid": "2010155203",
"programId": "600084782",
"needToken": false,
"mustToken": false,
"title": "CGTN 阿拉伯语频道",
"videoUrl": [
"http://livear.cgtn.com/1000a/prog_index.m3u8"
]
},
{
"id": 60,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
"pid": "600084744",
"sid": "2010152503",
"programId": "600084744",
"needToken": false,
"mustToken": false,
"title": "CGTN 西班牙语频道",
"videoUrl": [
"http://livees.cgtn.com/500e/prog_index.m3u8",
"http://livees.cgtn.com/1000e/prog_index.m3u8"
]
},
{
"id": 61,
"videoIndex": 0,
"channel": "国际频道",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
"pid": "600084781",
"sid": "2010155403",
"programId": "600084781",
"needToken": false,
"mustToken": false,
"title": "CGTN 纪录频道",
"videoUrl": [
"https://livedoc.cgtn.com/500d/prog_index.m3u8"
]
}
]
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="server_url">http://localhost:8080</string>
</resources>
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
+3 -3
View File
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '8.2.1' apply false id 'com.android.application' version '8.2.2' apply false
id 'com.android.library' version '8.2.1' apply false id 'com.android.library' version '8.2.2' apply false
id 'org.jetbrains.kotlin.android' version '1.9.21' apply false id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
} }