Compare commits

..
6 Commits
Author SHA1 Message Date
Li ZongYing 7b7db88422 test sign 2023-12-18 13:23:55 +08:00
Li ZongYing 4e3123019d remove guid store 2023-12-18 12:47:21 +08:00
Li ZongYing 9191d58513 backup source 2023-12-18 10:12:52 +08:00
Li ZongYing 434915fd3d keep screen on 2023-12-16 15:09:09 +08:00
Li ZongYing d8fffbb402 optimize ysp 2023-12-16 08:19:02 +08:00
Li ZongYing cc2dc25e35 optimize ysp 2023-12-15 18:12:11 +08:00
20 changed files with 184 additions and 79 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
- name: Run build with Gradle wrapper - name: Run build with Gradle wrapper
run: ./gradlew assembleRelease run: ./gradlew assembleRelease -PIS_SO_BUILD=false
- name: Sign app APK - name: Sign app APK
id: sign_app id: sign_app
+1 -1
View File
@@ -3,4 +3,4 @@
/cmake_install.cmake /cmake_install.cmake
/CMakeFiles/ /CMakeFiles/
/Makefile /Makefile
/src/main/cpp/native-lib.c /src/main/cpp/native.c
+8 -8
View File
@@ -15,17 +15,17 @@ if (IS_SO_BUILD)
# and CMake builds them for you. When you build your app, Gradle # and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK. # automatically packages shared libraries with your APK.
add_library( # Specifies the name of the library. add_library( # Specifies the name of the library.
native-lib native
# Sets the library as a shared library. # Sets the library as a shared library.
SHARED SHARED
# Provides a relative path to your source file(s). # Provides a relative path to your source file(s).
src/main/cpp/native-lib.c) src/main/cpp/native.c)
# 设置编译输出路径 # 设置编译输出路径
set_target_properties( set_target_properties(
native-lib native
PROPERTIES PROPERTIES
LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY
${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI} ${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI}
@@ -36,18 +36,18 @@ else ()
SHARED SHARED
src/main/cpp/nothing.c) src/main/cpp/nothing.c)
add_library(native-lib add_library(native
SHARED SHARED
IMPORTED) IMPORTED)
set_target_properties( # Specifies the target library. set_target_properties( # Specifies the target library.
native-lib native
# Specifies the parameter you want to define. # Specifies the parameter you want to define.
PROPERTIES IMPORTED_LOCATION PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import. # Provides the path to the library you want to import.
${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI}/libnative-lib.so) ${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI}/libnative.so)
endif () endif ()
add_library(libssl add_library(libssl
@@ -87,7 +87,7 @@ find_library( # Defines the name of the path variable that stores the
if (IS_SO_BUILD) if (IS_SO_BUILD)
# Links your native library against one or more other native libraries. # Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library. target_link_libraries( # Specifies the target library.
native-lib native
libssl libssl
libcrypto libcrypto
${log-lib}) ${log-lib})
@@ -96,6 +96,6 @@ else ()
nothing nothing
libssl libssl
libcrypto libcrypto
native-lib native
${log-lib}) ${log-lib})
endif () endif ()
+2 -3
View File
@@ -24,8 +24,7 @@ android {
// For ndk-build, instead use the ndkBuild block. // For ndk-build, instead use the ndkBuild block.
cmake { cmake {
// arguments "-DIS_SO_BUILD=${project.hasProperty('IS_SO_BUILD') ? project.IS_SO_BUILD : false}" arguments "-DIS_SO_BUILD=${project.hasProperty('IS_SO_BUILD') ? project.IS_SO_BUILD : true}"
arguments "-DIS_SO_BUILD=${false}"
abiFilters "armeabi-v7a", "arm64-v8a" abiFilters "armeabi-v7a", "arm64-v8a"
} }
@@ -104,7 +103,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
implementation 'androidx.core:core-ktx:1.11.0-beta02' implementation 'androidx.core:core-ktx:1.11.0-beta02'
implementation 'androidx.leanback:leanback:1.0.0' implementation 'androidx.leanback:leanback:1.2.0-alpha02'
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC")
+1
View File
@@ -14,6 +14,7 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MyTV"> android:theme="@style/Theme.MyTV">
<activity <activity
android:keepScreenOn="true"
android:name=".MainActivity" android:name=".MainActivity"
android:banner="@drawable/tv" android:banner="@drawable/tv"
android:exported="true" android:exported="true"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,7 +11,7 @@ class Encryptor {
companion object { companion object {
init { init {
System.loadLibrary("native-lib") System.loadLibrary("native")
} }
} }
} }
@@ -17,13 +17,13 @@ import androidx.media3.exoplayer.hls.HlsMediaSource
import java.io.IOException import java.io.IOException
open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAdapter() { open class ExoPlayerAdapter(private var mContext: Context?) : PlayerAdapter() {
val mPlayer = mContext?.let { ExoPlayer.Builder(it).build() } val mPlayer = mContext?.let { ExoPlayer.Builder(it).build() }
var mSurfaceHolderGlueHost: SurfaceHolderGlueHost? = null var mSurfaceHolderGlueHost: SurfaceHolderGlueHost? = null
val mRunnable: Runnable = object : Runnable { val mRunnable: Runnable = object : Runnable {
override fun run() { override fun run() {
callback.onCurrentPositionChanged(this@Custom2MediaPlayerAdapter) callback.onCurrentPositionChanged(this@ExoPlayerAdapter)
mHandler.postDelayed(this, getProgressUpdatingInterval().toLong()) mHandler.postDelayed(this, getProgressUpdatingInterval().toLong())
} }
}; };
@@ -38,7 +38,7 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
open fun notifyBufferingStartEnd() { open fun notifyBufferingStartEnd() {
callback.onBufferingStateChanged( callback.onBufferingStateChanged(
this@Custom2MediaPlayerAdapter, this@ExoPlayerAdapter,
mBufferingStart || !mInitialized mBufferingStart || !mInitialized
) )
} }
@@ -65,7 +65,7 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
mInitialized = false mInitialized = false
notifyBufferingStartEnd() notifyBufferingStartEnd()
if (mHasDisplay) { if (mHasDisplay) {
callback.onPreparedStateChanged(this@Custom2MediaPlayerAdapter) callback.onPreparedStateChanged(this@ExoPlayerAdapter)
} }
} }
} }
@@ -100,11 +100,11 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
mPlayer?.setVideoSurfaceHolder(surfaceHolder) mPlayer?.setVideoSurfaceHolder(surfaceHolder)
if (mHasDisplay) { if (mHasDisplay) {
if (mInitialized) { if (mInitialized) {
callback.onPreparedStateChanged(this@Custom2MediaPlayerAdapter) callback.onPreparedStateChanged(this@ExoPlayerAdapter)
} }
} else { } else {
if (mInitialized) { if (mInitialized) {
callback.onPreparedStateChanged(this@Custom2MediaPlayerAdapter) callback.onPreparedStateChanged(this@ExoPlayerAdapter)
} }
} }
} }
@@ -154,14 +154,14 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
return return
} }
mPlayer?.play() mPlayer?.play()
callback.onPlayStateChanged(this@Custom2MediaPlayerAdapter) callback.onPlayStateChanged(this@ExoPlayerAdapter)
callback.onCurrentPositionChanged(this@Custom2MediaPlayerAdapter) callback.onCurrentPositionChanged(this@ExoPlayerAdapter)
} }
override fun pause() { override fun pause() {
if (isPlaying) { if (isPlaying) {
mPlayer?.pause() mPlayer?.pause()
callback.onPlayStateChanged(this@Custom2MediaPlayerAdapter) callback.onPlayStateChanged(this@ExoPlayerAdapter)
} }
} }
@@ -222,7 +222,7 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
} }
mPlayer?.prepare() mPlayer?.prepare()
mPlayer?.playWhenReady = true mPlayer?.playWhenReady = true
callback.onPlayStateChanged(this@Custom2MediaPlayerAdapter) callback.onPlayStateChanged(this@ExoPlayerAdapter)
} }
/** /**
@@ -234,7 +234,7 @@ open class Custom2MediaPlayerAdapter(private var mContext: Context?) : PlayerAda
} }
} }
internal class VideoPlayerSurfaceHolderCallback(private val playerAdapter: Custom2MediaPlayerAdapter) : internal class VideoPlayerSurfaceHolderCallback(private val playerAdapter: ExoPlayerAdapter) :
SurfaceHolder.Callback { SurfaceHolder.Callback {
override fun surfaceCreated(surfaceHolder: SurfaceHolder) { override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
@@ -11,6 +11,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
@@ -32,6 +33,8 @@ class MainActivity : FragmentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, playbackFragment) .add(R.id.main_browse_fragment, playbackFragment)
@@ -40,6 +43,8 @@ class MainActivity : FragmentActivity() {
.hide(infoFragment) .hide(infoFragment)
.commit() .commit()
} }
Log.i(TAG, "Signature ${getAppSignature()}")
} }
fun switchInfoFragment(tv: TV) { fun switchInfoFragment(tv: TV) {
@@ -36,6 +36,16 @@ class MainFragment : BrowseSupportFragment() {
private var sharedPref: SharedPreferences? = null private var sharedPref: SharedPreferences? = null
// override fun onCreateView(
// inflater: LayoutInflater,
// container: ViewGroup?,
// savedInstanceState: Bundle?
// ): View? {
// super.onCreate()
// // 使用自定义的布局文件
// return inflater.inflate(R.layout.custom_browse_fragment, container, false)
// }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
@@ -47,15 +57,16 @@ class MainFragment : BrowseSupportFragment() {
setupEventListeners() setupEventListeners()
view?.post { view?.post {
request?.fetchPage() // request?.fetchPage()
} }
tvListViewModel.getListLiveData().value?.forEach { tvViewModel -> tvListViewModel.getListLiveData().value?.forEach { tvViewModel ->
tvViewModel.videoUrl.observe(viewLifecycleOwner) { _ -> tvViewModel.videoIndex.observe(viewLifecycleOwner) { videoIndex ->
Log.i(TAG, "tv ${tvViewModel.getTV()}") if (tvViewModel.getIsFirstTime()) {
if (tvViewModel.updateByYSP()) { tvViewModel.isFirstTime(false)
} else {
val tv = tvViewModel.getTV() val tv = tvViewModel.getTV()
if (tv.id == itemPosition) { if (tvViewModel.id.value == itemPosition) {
(activity as? MainActivity)?.play(tv) (activity as? MainActivity)?.play(tv)
// (activity as? MainActivity)?.switchInfoFragment(tv) // (activity as? MainActivity)?.switchInfoFragment(tv)
} }
@@ -66,7 +77,6 @@ class MainFragment : BrowseSupportFragment() {
private fun setupUIElements() { private fun setupUIElements() {
brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background) brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background)
// headersState = HEADERS_DISABLED
} }
private fun updateRows(tv: TV) { private fun updateRows(tv: TV) {
@@ -87,7 +97,6 @@ class MainFragment : BrowseSupportFragment() {
rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
// val cardPresenter = CardPresenter(lifecycleScope, viewLifecycleOwner)
val cardPresenter = CardPresenter(lifecycleScope) val cardPresenter = CardPresenter(lifecycleScope)
var idx: Long = 0 var idx: Long = 0
@@ -112,6 +121,12 @@ class MainFragment : BrowseSupportFragment() {
adapter = rowsAdapter adapter = rowsAdapter
itemPosition = sharedPref?.getInt("position", 0)!! itemPosition = sharedPref?.getInt("position", 0)!!
if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0
savePosition(0)
}
val tv = list2[itemPosition].item
val tvModel = tvListViewModel.getTVModel(itemPosition) val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) { if (tvModel?.ysp() != null) {
@@ -119,10 +134,15 @@ class MainFragment : BrowseSupportFragment() {
tvModel.let { request?.fetchData(it) } tvModel.let { request?.fetchData(it) }
} }
} else { } else {
(activity as? MainActivity)?.play(list2[itemPosition].item) (activity as? MainActivity)?.play(tv)
// (activity as? MainActivity)?.switchInfoFragment(list2[itemPosition].item) // (activity as? MainActivity)?.switchInfoFragment(tv)
} }
Toast.makeText(
activity,
tv.title,
Toast.LENGTH_SHORT
).show()
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
@@ -151,6 +171,7 @@ class MainFragment : BrowseSupportFragment() {
val tvModel = tvListViewModel.getTVModel(itemPosition) val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) { if (tvModel?.ysp() != null) {
Log.i(TAG, "${tvModel.title} to get ysp")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) } tvModel.let { request?.fetchData(it) }
} }
@@ -182,6 +203,7 @@ class MainFragment : BrowseSupportFragment() {
val tvModel = tvListViewModel.getTVModel(itemPosition) val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) { if (tvModel?.ysp() != null) {
Log.i(TAG, "${tvModel.title} to get ysp")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) } tvModel.let { request?.fetchData(it) }
} }
@@ -250,6 +272,7 @@ class MainFragment : BrowseSupportFragment() {
val tvModel = tvListViewModel.getTVModel(itemPosition) val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) { if (tvModel?.ysp() != null) {
Log.i(TAG, "${tvModel.title} to get ysp")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) } tvModel.let { request?.fetchData(it) }
} }
@@ -13,13 +13,13 @@ import java.io.IOException
class PlaybackFragment : VideoSupportFragment() { class PlaybackFragment : VideoSupportFragment() {
private lateinit var mTransportControlGlue: PlaybackTransportControlGlue<PlayerAdapter> private lateinit var mTransportControlGlue: PlaybackTransportControlGlue<PlayerAdapter>
private var playerAdapter: Custom2MediaPlayerAdapter? = null private var playerAdapter: ExoPlayerAdapter? = null
private var lastVideoUrl: String = "" private var lastVideoUrl: String = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
playerAdapter = Custom2MediaPlayerAdapter(context) playerAdapter = ExoPlayerAdapter(context)
playerAdapter?.setRepeatAction(PlaybackControlsRow.RepeatAction.INDEX_NONE) playerAdapter?.setRepeatAction(PlaybackControlsRow.RepeatAction.INDEX_NONE)
view?.isFocusable = false view?.isFocusable = false
@@ -1,6 +1,7 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context import android.content.Context
import android.util.Base64
import android.util.Log import android.util.Log
import com.lizongying.mytv.api.ApiClient import com.lizongying.mytv.api.ApiClient
import com.lizongying.mytv.api.LiveInfo import com.lizongying.mytv.api.LiveInfo
@@ -14,6 +15,9 @@ import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class Request(var context: Context) { class Request(var context: Context) {
@@ -33,17 +37,17 @@ class Request(var context: Context) {
"CCTV10" to "CCTV10 科教", "CCTV10" to "CCTV10 科教",
"CCTV11" to "CCTV11 戏曲", "CCTV11" to "CCTV11 戏曲",
"CCTV12" to "CCTV12 社会与法", "CCTV12" to "CCTV12 社会与法",
// "CCTV13" to "CCTV13", "CCTV13" to "CCTV13 新闻",
"CCTV14" to "CCTV14 少儿", "CCTV14" to "CCTV14 少儿",
"CCTV15" to "CCTV15 音乐", "CCTV15" to "CCTV15 音乐",
"CCTV16-HD" to "CCTV16 奥林匹克", "CCTV16-HD" to "CCTV16 奥林匹克",
"CCTV17" to "CCTV17 农业农村", "CCTV17" to "CCTV17 农业农村",
"CGTN" to "CGTN", "CGTN" to "CGTN",
"CGTN外语纪录频道" to "CGTN 纪录频道",
"CGTN法语频道" to "CGTN 法语频道", "CGTN法语频道" to "CGTN 法语频道",
"CGTN俄语频道" to "CGTN 俄语频道", "CGTN俄语频道" to "CGTN 俄语频道",
"CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道", "CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道",
"CGTN西班牙语频道" to "CGTN 西班牙语频道", "CGTN西班牙语频道" to "CGTN 西班牙语频道",
// "CGTN外语纪录频道" to "CGTN外语纪录频道",
"东方卫视" to "东方卫视", "东方卫视" to "东方卫视",
"湖南卫视" to "湖南卫视", "湖南卫视" to "湖南卫视",
@@ -78,6 +82,7 @@ class Request(var context: Context) {
fun fetchData(tvModel: TVViewModel) { fun fetchData(tvModel: TVViewModel) {
val data = ysp?.switch(tvModel) val data = ysp?.switch(tvModel)
val title = tvModel.getTV().title
val request = data?.let { LiveInfoRequest(it) } val request = data?.let { LiveInfoRequest(it) }
request?.let { yspApiService?.getLiveInfo(it) } request?.let { yspApiService?.getLiveInfo(it) }
@@ -86,12 +91,41 @@ class Request(var context: Context) {
if (response.isSuccessful) { if (response.isSuccessful) {
val liveInfo = response.body() val liveInfo = response.body()
if (liveInfo?.data?.playurl != null) { if (liveInfo?.data?.playurl != null) {
tvModel.updateVideoUrlByYSP(liveInfo.data.playurl) val chanll = liveInfo.data.chanll
val decodedBytes = Base64.decode(
chanll.substring(9, chanll.length - 3),
Base64.DEFAULT
)
val decodedString = String(decodedBytes)
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val matchResult = regex.find(decodedString)
if (matchResult != null) {
val (key, iv) = matchResult.destructured
val keyBytes = Base64.decode(key, Base64.DEFAULT)
val ivBytes = Base64.decode(iv, Base64.DEFAULT)
val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
ivBytes
).uppercase()
Log.i(TAG, "$title url $url")
tvModel.addVideoUrl(url)
} else {
Log.e(TAG, "$title key error")
tvModel.firstSource()
}
} else {
Log.e(TAG, "$title url error $request")
tvModel.firstSource()
} }
} else {
Log.e(TAG, "$title status error")
tvModel.firstSource()
} }
} }
override fun onFailure(call: Call<LiveInfo>, t: Throwable) { override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
Log.e(TAG, "${tvModel.getTV().title} request error")
tvModel.firstSource()
} }
}) })
} }
@@ -152,6 +186,22 @@ class Request(var context: Context) {
}) })
} }
private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String {
var plaintext =
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,AppleM1Pro,OpenGL4.1)"}"""
return try {
val keySpec = SecretKeySpec(key, "DESede")
val ivSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encryptedBytes = cipher.doFinal(plaintext.toByteArray())
return encryptedBytes.let { it -> it.joinToString("") { "%02x".format(it) } }
} catch (e: Exception) {
e.printStackTrace()
""
}
}
companion object { companion object {
private const val TAG = "Request" private const val TAG = "Request"
} }
+16 -21
View File
@@ -1,7 +1,5 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.util.Log
object TVList { object TVList {
val list: Map<String, Map<String, TV>> by lazy { val list: Map<String, Map<String, TV>> by lazy {
setupTV() setupTV()
@@ -17,34 +15,31 @@ CCTV1 综合,http://tvpull.dxhmt.cn/tv/11481-4.m3u8,https://resources.yangshipin
CCTV2 财经,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp,600001800,2000203603 CCTV2 财经,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp,600001800,2000203603
CCTV3 综艺,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8 CCTV3 综艺,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8
CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8;http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp,600001814,2000204803 CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8;http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp,600001814,2000204803
CCTV4 中文国际美洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt= CCTV5 体育,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226395/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp,600001818,2000205103
CCTV4 中文国际欧洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt= CCTV5+ 体育赛事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226221/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp,600001817,2000204503
CCTV5 体育,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp,600001818,2000205103 CCTV6 电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226393/index.m3u8
CCTV5+ 体育赛事,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp,600001817,2000204503 CCTV7 国防军事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226192/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp,600004092,2000510003
CCTV6 电影,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv6hd/3000/index.m3u8?&encrypt= CCTV8 电视剧,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226391/index.m3u8
CCTV7 国防军事,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv7hd/51/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp,600004092,2000510003 CCTV9 记录,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp,600004078,2000499403
CCTV8 电视剧,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv8hd/3000/index.m3u8?&encrypt= CCTV10 科教,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226189/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp,600001805,2000203503
CCTV9 记录,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv9hd/57/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp,600004078,2000499403 CCTV11 戏曲,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226240/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp,600001806,2000204103
CCTV10 科教,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/cctv10hd/2000/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp,600001805,2000203503 CCTV12 社会与法,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226190/index.m3u8, https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp,600001807,2000202603
CCTV11 戏曲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv11hd/57/20200103/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp,600001806,2000204103 CCTV13 新闻,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8
CCTV12 社会与法,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv12hd/57/index.m3u8?&encrypt=, https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp,600001807,2000202603 CCTV14 少儿,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp,600001809,2000204403
CCTV13 新闻,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8;http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctv13/2000/index.m3u8?&encrypt= CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp,600001815,2000205003
CCTV14 少儿,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8;http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv14hd/3000/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp,600001809,2000204403
CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8;http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv15hd/51/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp,600001815,2000205003
CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp,600098637,2012375003 CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp,600098637,2012375003
CCTV17 农业农村,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv17hd/57/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp,600001810,2000204203 CCTV17 农业农村,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp,600001810,2000204203
CGTN,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctvnews/1000/index.m3u8?&encrypt=,https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp,600014550,2001656803 CGTN,,https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp,600014550,2001656803
CGTN 新闻频道,http://live.cgtn.com/1000/prog_index.m3u8 CGTN 新闻频道,http://live.cgtn.com/1000/prog_index.m3u8
CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8 CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
CGTN 法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp,600084704,2010153503 CGTN 法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp,600084704,2010153503
CGTN 俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp,600084758,2010152603 CGTN 俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp,600084758,2010152603
CGTN 西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp,600084744,2010152503 CGTN 西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8;http://livees.cgtn.com/1000e/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp,600084744,2010152503
CGTN 阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp,600084782,2010155203 CGTN 阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp,600084782,2010155203
CGTN 拉丁美洲频道,http://livees.cgtn.com/1000e/prog_index.m3u8
书画频道,http://211.103.180.178:8234/live_hls/hdmi.m3u8 书画频道,http://211.103.180.178:8234/live_hls/hdmi.m3u8
地方频道 地方频道
东方卫视,,https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp,600002483,2000292403 东方卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226217/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp,600002483,2000292403
内蒙古卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226389/index.m3u8 内蒙古卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226389/index.m3u8
湖南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp,600002475,2000296203 湖南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp,600002475,2000296203
湖北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp,600002508,2000294503 湖北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp,600002508,2000294503
@@ -13,7 +13,8 @@ data class LiveInfo(
) )
data class LiveInfoData( data class LiveInfoData(
val playurl: String val chanll: String,
val playurl: String,
) )
data class LiveInfoRequest( data class LiveInfoRequest(
@@ -68,7 +68,7 @@ class YSP(var context: Context) {
cnlid = tvModel.getTV().sid cnlid = tvModel.getTV().sid
defn = "fhd" defn = "fhd"
guid = getGuid() // guid = getGuid()
randStr = getRand() randStr = getRand()
timeStr = getTimeStr() timeStr = getTimeStr()
@@ -80,7 +80,6 @@ class YSP(var context: Context) {
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"}"""
} }
private fun getTimeStr(): String { private fun getTimeStr(): String {
@@ -100,8 +99,8 @@ class YSP(var context: Context) {
} }
private fun getGuid(): String { private fun getGuid(): String {
// var guid = sharedPref?.getString("guid", "").toString() var guid = sharedPref?.getString("guid", "")
if (guid == "") { if (guid == null || guid.length < 18) {
guid = generateGuid() guid = generateGuid()
with(sharedPref!!.edit()) { with(sharedPref!!.edit()) {
putString("guid", guid) putString("guid", guid)
@@ -1,6 +1,5 @@
package com.lizongying.mytv.models package com.lizongying.mytv.models
import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.lizongying.mytv.TV import com.lizongying.mytv.TV
@@ -41,4 +40,8 @@ class TVListViewModel : ViewModel() {
fun getTVModel(id: Int): TVViewModel? { fun getTVModel(id: Int): TVViewModel? {
return tvModelListLiveData.value?.get(id) return tvModelListLiveData.value?.get(id)
} }
fun size(): Int {
return tvListLiveData.value!!.size
}
} }
@@ -1,5 +1,6 @@
package com.lizongying.mytv.models package com.lizongying.mytv.models
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@@ -8,7 +9,7 @@ import com.lizongying.mytv.TV
class TVViewModel(private var tv: TV) : ViewModel() { class TVViewModel(private var tv: TV) : ViewModel() {
private var mapping = mapOf( private var mapping = mapOf(
"CCTV4K" to "CCTV4K", "CCTV4K" to "CCTV4K",
// "CCTV1" to "CCTV1 综合", "CCTV1" to "CCTV1 综合",
"CCTV2" to "CCTV2 财经", "CCTV2" to "CCTV2 财经",
"CCTV4" to "CCTV4 中文国际", "CCTV4" to "CCTV4 中文国际",
"CCTV5" to "CCTV5 体育", "CCTV5" to "CCTV5 体育",
@@ -18,17 +19,17 @@ class TVViewModel(private var tv: TV) : ViewModel() {
"CCTV10" to "CCTV10 科教", "CCTV10" to "CCTV10 科教",
"CCTV11" to "CCTV11 戏曲", "CCTV11" to "CCTV11 戏曲",
"CCTV12" to "CCTV12 社会与法", "CCTV12" to "CCTV12 社会与法",
// "CCTV13" to "CCTV13", "CCTV13" to "CCTV13 新闻",
"CCTV14" to "CCTV14 少儿", "CCTV14" to "CCTV14 少儿",
"CCTV15" to "CCTV15 音乐", "CCTV15" to "CCTV15 音乐",
"CCTV16-HD" to "CCTV16 奥林匹克", "CCTV16-HD" to "CCTV16 奥林匹克",
"CCTV17" to "CCTV17 农业农村", "CCTV17" to "CCTV17 农业农村",
"CGTN" to "CGTN", "CGTN" to "CGTN",
"CGTN外语纪录频道" to "CGTN 纪录频道",
"CGTN法语频道" to "CGTN 法语频道", "CGTN法语频道" to "CGTN 法语频道",
"CGTN俄语频道" to "CGTN 俄语频道", "CGTN俄语频道" to "CGTN 俄语频道",
"CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道", "CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道",
"CGTN西班牙语频道" to "CGTN 西班牙语频道", "CGTN西班牙语频道" to "CGTN 西班牙语频道",
// "CGTN外语纪录频道" to "CGTN外语纪录频道",
"东方卫视" to "东方卫视", "东方卫视" to "东方卫视",
"湖南卫视" to "湖南卫视", "湖南卫视" to "湖南卫视",
@@ -53,12 +54,24 @@ class TVViewModel(private var tv: TV) : ViewModel() {
"海南卫视" to "海南卫视", "海南卫视" to "海南卫视",
).entries.associate { (key, value) -> value to key } ).entries.associate { (key, value) -> value to key }
private var updateByYSP = false private var isFirstTime = true
private val _id = MutableLiveData<Int>()
val id: LiveData<Int>
get() = _id
private val _title = MutableLiveData<String>()
val title: LiveData<String>
get() = _title
private val _videoUrl = MutableLiveData<List<String>>() private val _videoUrl = MutableLiveData<List<String>>()
val videoUrl: LiveData<List<String>> val videoUrl: LiveData<List<String>>
get() = _videoUrl get() = _videoUrl
private val _videoIndex = MutableLiveData<Int>()
val videoIndex: LiveData<Int>
get() = _videoIndex
private val _pid = MutableLiveData<String>() private val _pid = MutableLiveData<String>()
val pid: LiveData<String> val pid: LiveData<String>
get() = _pid get() = _pid
@@ -79,25 +92,37 @@ class TVViewModel(private var tv: TV) : ViewModel() {
_backgroundImage.value = url _backgroundImage.value = url
} }
private fun updateVideoUrl(url: String) { fun addVideoUrl(url: String) {
tv.videoUrl = listOf(url) tv.videoUrl = tv.videoUrl + listOf(url)
tv.videoIndex = 0 tv.videoIndex = tv.videoUrl.lastIndex
_videoUrl.value = listOf(url) _videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoIndex
} }
fun updateVideoUrlByYSP(url: String) { fun firstSource() {
updateByYSP = true if (tv.videoUrl.isNotEmpty()) {
updateVideoUrl(url) tv.videoIndex = 0
_videoIndex.value = 0
} else {
Log.e(TAG, "no first")
}
} }
init { init {
_id.value = tv.id
_title.value = tv.title
_videoUrl.value = tv.videoUrl _videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoIndex
_pid.value = tv.pid _pid.value = tv.pid
_sid.value = tv.sid _sid.value = tv.sid
} }
fun updateByYSP(): Boolean { fun getIsFirstTime(): Boolean {
return updateByYSP return isFirstTime
}
fun isFirstTime(firstTime: Boolean) {
isFirstTime = firstTime
} }
fun update(t: TV) { fun update(t: TV) {
@@ -114,4 +139,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
} }
return mapping[tv.title] return mapping[tv.title]
} }
companion object {
private const val TAG = "TVViewModel"
}
} }