Compare commits

..
7 Commits
Author SHA1 Message Date
Li ZongYing b2c2847ae0 add epg 2023-12-21 17:51:26 +08:00
Li ZongYing 0285f1b3b1 remove left headers 2023-12-20 14:55:39 +08:00
Li ZongYing e6fa9a50db change sign 2023-12-18 13:37:27 +08:00
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
27 changed files with 669 additions and 373 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.
@@ -1,47 +1,22 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context import android.graphics.Color
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import android.util.Log
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import androidx.leanback.widget.ImageCardView import androidx.leanback.widget.ImageCardView
import androidx.leanback.widget.Presenter import androidx.leanback.widget.Presenter
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CardPresenter( class CardPresenter(
private val lifecycleScope: LifecycleCoroutineScope, private val owner: LifecycleOwner,
// private val owner: LifecycleOwner
) : Presenter() { ) : Presenter() {
private var tvViewModel: TVViewModel? = null
fun bindData(viewModel: TVViewModel, fragment: Context?, viewHolder: ViewHolder) {
}
fun setViewModel(model: TVViewModel) {
tvViewModel = model
}
fun unbind() {
// viewModel?.backgroundImage?.removeObservers(fragment)
}
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
val cardView = object : val cardView = object :
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) { ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
override fun setSelected(selected: Boolean) {
updateCardBackgroundColor(this)
super.setSelected(selected)
}
}
cardView.isFocusable = true cardView.isFocusable = true
cardView.isFocusableInTouchMode = true cardView.isFocusableInTouchMode = true
@@ -49,70 +24,39 @@ class CardPresenter(
} }
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) { override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
val tv = item as TV val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView val cardView = viewHolder.view as ImageCardView
cardView.titleText = tv.title cardView.titleText = tvViewModel.title.value
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
cardView.tag = tv.videoUrl cardView.tag = tvViewModel.videoUrl.value
// lifecycleScope.launch(Dispatchers.IO) { if (tvViewModel.logo.value != null) {
// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) } Glide.with(viewHolder.view.context)
// .load(tvViewModel.logo.value)
// withContext(Dispatchers.Main) { .centerInside()
// cardView.mainImageView.setImageBitmap(videoThumbnail) .into(cardView.mainImageView)
// }
// }
// tvViewModel?.update(tv) cardView.setBackgroundColor(Color.WHITE)
// tvViewModel?.backgroundImage?.observe(owner) { imageUrl -> cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
// if (imageUrl != "") { }
// Glide.with(viewHolder.view.context)
// .load(imageUrl)
// .centerCrop()
// .into(cardView.mainImageView)
// }
// }
}
override fun onUnbindViewHolder(viewHolder: ViewHolder) { tvViewModel.program.observe(owner) { _ ->
val cardView = viewHolder.view as ImageCardView val program = tvViewModel.getProgramOne()
cardView.badgeImage = null if (program != null) {
cardView.mainImage = null cardView.contentText = program.name
}
private fun updateCardBackgroundColor(view: ImageCardView) {
val currentTag = view.tag
lifecycleScope.launch(Dispatchers.IO) {
delay(1000)
if (view.isSelected && view.tag != null && currentTag == view.tag) {
val videoThumbnail = view.tag.toString().let { getVideoThumbnail(it) }
withContext(Dispatchers.Main) {
if (view.isSelected && currentTag == view.tag) {
view.mainImageView.setImageBitmap(videoThumbnail)
}
}
} }
} }
} }
private fun getVideoThumbnail(url: String): Bitmap? { override fun onUnbindViewHolder(viewHolder: ViewHolder) {
val mediaMetadataRetriever = MediaMetadataRetriever() val cardView = viewHolder.view as ImageCardView
try { cardView.mainImage = null
val map = HashMap<String, String>()
mediaMetadataRetriever.setDataSource(url, map)
return mediaMetadataRetriever.frameAtTime
} catch (e: Exception) {
e.printStackTrace()
} finally {
mediaMetadataRetriever.release()
}
return null
} }
companion object { companion object {
private const val TAG = "CardPresenter" private const val TAG = "CardPresenter"
private const val CARD_WIDTH = 313 private const val CARD_WIDTH = 300
private const val CARD_HEIGHT = 176 private const val CARD_HEIGHT = 101
} }
} }
@@ -11,7 +11,7 @@ class Encryptor {
companion object { companion object {
init { init {
System.loadLibrary("native-lib") System.loadLibrary("native")
} }
} }
} }
@@ -10,6 +10,8 @@ import androidx.leanback.media.PlaybackGlueHost
import androidx.leanback.media.PlayerAdapter import androidx.leanback.media.PlayerAdapter
import androidx.leanback.media.SurfaceHolderGlueHost import androidx.leanback.media.SurfaceHolderGlueHost
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
@@ -176,6 +178,14 @@ open class ExoPlayerAdapter(private var mContext: Context?) : PlayerAdapter() {
return mBufferedProgress return mBufferedProgress
} }
private var mPlayerErrorListener: PlayerErrorListener? = null
private inner class PlayerErrorListener : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
callback.onError(this@ExoPlayerAdapter, error.errorCode, error.message)
}
}
/** /**
* Sets the media source of the player witha given URI. * Sets the media source of the player witha given URI.
* *
@@ -189,6 +199,13 @@ open class ExoPlayerAdapter(private var mContext: Context?) : PlayerAdapter() {
} }
mMediaSourceUri = uri mMediaSourceUri = uri
prepareMediaForPlaying() prepareMediaForPlaying()
mPlayer?.playWhenReady = true
if (mPlayerErrorListener == null) {
mPlayerErrorListener = PlayerErrorListener()
mPlayer?.addListener(mPlayerErrorListener!!)
}
return true return true
} }
@@ -221,7 +238,7 @@ open class ExoPlayerAdapter(private var mContext: Context?) : PlayerAdapter() {
throw RuntimeException(e) throw RuntimeException(e)
} }
mPlayer?.prepare() mPlayer?.prepare()
mPlayer?.playWhenReady = true
callback.onPlayStateChanged(this@ExoPlayerAdapter) callback.onPlayStateChanged(this@ExoPlayerAdapter)
} }
@@ -232,6 +249,10 @@ open class ExoPlayerAdapter(private var mContext: Context?) : PlayerAdapter() {
override fun isPrepared(): Boolean { override fun isPrepared(): Boolean {
return mInitialized && (mSurfaceHolderGlueHost == null || mHasDisplay) return mInitialized && (mSurfaceHolderGlueHost == null || mHasDisplay)
} }
companion object {
private const val TAG = "ExoPlayerAdapter"
}
} }
internal class VideoPlayerSurfaceHolderCallback(private val playerAdapter: ExoPlayerAdapter) : internal class VideoPlayerSurfaceHolderCallback(private val playerAdapter: ExoPlayerAdapter) :
@@ -1,9 +0,0 @@
package com.lizongying.mytv
import java.io.Serializable
data class Info(
var rowPosition: Int = 0,
var itemPosition: Int = 0,
var item: TV,
) : Serializable
@@ -11,12 +11,14 @@ 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
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.lizongying.mytv.models.TVViewModel
import java.security.MessageDigest import java.security.MessageDigest
@@ -32,6 +34,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)
@@ -63,9 +67,9 @@ class MainActivity : FragmentActivity() {
.commit() .commit()
} }
fun play(tv: TV) { fun play(tvViewModel: TVViewModel) {
Log.i(TAG, "play: $tv") Log.i(TAG, "play: ${tvViewModel.getTV()}")
playbackFragment.play(tv) playbackFragment.play(tvViewModel)
} }
fun prev() { fun prev() {
@@ -168,30 +172,57 @@ class MainActivity : FragmentActivity() {
} }
KeyEvent.KEYCODE_DPAD_CENTER -> { KeyEvent.KEYCODE_DPAD_CENTER -> {
Log.i(TAG, "KEYCODE_DPAD_CENTER")
switchMainFragment() switchMainFragment()
} }
KeyEvent.KEYCODE_DPAD_UP -> { KeyEvent.KEYCODE_DPAD_UP -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prev() prev()
} else {
if (mainFragment.selectedPosition == 0) {
mainFragment.setSelectedPosition(
mainFragment.tvListViewModel.maxNum.size - 1,
false
)
}
} }
} }
KeyEvent.KEYCODE_DPAD_DOWN -> { KeyEvent.KEYCODE_DPAD_DOWN -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
next() next()
} else {
if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) {
// mainFragment.setSelectedPosition(0, false)
hideMainFragment()
return false
}
} }
} }
KeyEvent.KEYCODE_DPAD_LEFT -> { KeyEvent.KEYCODE_DPAD_LEFT -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prevSource() prevSource()
} else {
if (mainFragment.tvListViewModel.getTVViewModelCurrent()
?.getItemPosition() == 0
) {
// mainFragment.toLastPosition()
hideMainFragment()
}
} }
} }
KeyEvent.KEYCODE_DPAD_RIGHT -> { KeyEvent.KEYCODE_DPAD_RIGHT -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
nextSource() nextSource()
} else {
if (mainFragment.tvListViewModel.getTVViewModelCurrent()
?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1
) {
mainFragment.toFirstPosition()
}
} }
} }
} }
@@ -19,22 +19,25 @@ import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter import androidx.leanback.widget.RowPresenter
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.models.TVListViewModel import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class MainFragment : BrowseSupportFragment() { class MainFragment : BrowseSupportFragment() {
var itemPosition: Int = 0 var itemPosition: Int = 0
private val list2: MutableList<Info> = mutableListOf()
private var request: Request? = null private var request: Request? = null
private var rowsAdapter: ArrayObjectAdapter? = null private var rowsAdapter: ArrayObjectAdapter? = null
private var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private var sharedPref: SharedPreferences? = null private var sharedPref: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
headersState = HEADERS_DISABLED
}
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
@@ -48,61 +51,120 @@ class MainFragment : BrowseSupportFragment() {
view?.post { view?.post {
// request?.fetchPage() // request?.fetchPage()
// tvListViewModel.getTVViewModel(0)?.let { request?.fetchProgram(it) }
} }
tvListViewModel.getListLiveData().value?.forEach { tvViewModel -> tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel ->
tvViewModel.videoUrl.observe(viewLifecycleOwner) { _ -> tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
// Log.i(TAG, "tv ${tvViewModel.getTV()}") if (tvViewModel.ready.value != null) {
if (tvViewModel.updateByYSP()) { Log.i(TAG, "ready ${tvViewModel.title.value}")
val tv = tvViewModel.getTV() if (tvViewModel.id.value == itemPosition) {
if (tv.id == itemPosition) { (activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.play(tv)
// (activity as? MainActivity)?.switchInfoFragment(tv) // (activity as? MainActivity)?.switchInfoFragment(tv)
} }
} }
} }
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) {
Log.i(TAG, "switch to ${tvViewModel.title.value}")
if (tvViewModel.ysp() != null) {
Log.i(TAG, "${tvViewModel.title.value} to get ysp")
lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { request?.fetchData(it) }
}
} else {
(activity as? MainActivity)?.play(tvViewModel)
// (activity as? MainActivity)?.switchInfoFragment(item)
}
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
Toast.makeText(
activity,
tvViewModel.title.value,
Toast.LENGTH_SHORT
).show()
}
}
tvViewModel.program.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.program.value == null || tvViewModel.program.value?.size!! < 3) {
if (tvViewModel.programId.value != null) {
Log.i(TAG, "get program ${tvViewModel.title.value}")
request?.fetchProgram(tvViewModel)
}
}
}
} }
} }
fun toLastPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(tvListViewModel.maxNum[selectedPosition] - 1)
)
}
fun toFirstPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(0)
)
}
override fun startHeadersTransition(withHeaders: Boolean) {
// check(mCanShowHeaders) { "Cannot start headers transition" }
// if (isInHeadersTransition || mShowingHeaders == withHeaders) {
// return
// }
// startHeadersTransitionInternal(withHeaders)
}
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 // var headers = headersSupportFragment
// headers.setMenuVisibility(false)
// Log.i(TAG, "headers $headers")
// setHeadersState(HEADERS_DISABLED);
//
// setHeaderPresenterSelector(object : PresenterSelector() {
// override fun getPresenter(o: Any): Presenter {
// return IconHeaderItemPresenter()
// }
// })
// showHeaders(true)
} }
private fun updateRows(tv: TV) { // private fun updateRows(tv: TV) {
// 获取适配器中的数据 //// 获取适配器中的数据
val dataList = rowsAdapter?.replace(tv.id, tv) // val dataList = rowsAdapter?.replace(tv.id, tv)
// ////
//// 修改数据 ////// 修改数据
//// 这里假设 dataList 是一个可变的列表 ////// 这里假设 dataList 是一个可变的列表
// dataList[position] = updatedData //// dataList[position] = updatedData
// ////
//// 刷新适配器 ////// 刷新适配器
// rowsAdapter.notifyItemChanged(position) //// rowsAdapter.notifyItemChanged(position)
// rowsAdapter.notifyItemRangeChanged() //// rowsAdapter.notifyItemRangeChanged()
} // }
private fun loadRows() { private fun loadRows() {
val list = TVList.list
rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
val cardPresenter = CardPresenter(lifecycleScope) val cardPresenter = CardPresenter(viewLifecycleOwner)
var idx: Long = 0 var idx: Long = 0
for ((k, v) in list) { for ((k, v) in TVList.list) {
val listRowAdapter = ArrayObjectAdapter(cardPresenter) val listRowAdapter = ArrayObjectAdapter(cardPresenter)
var idx2 = 0 for ((idx2, v1) in v.withIndex()) {
for ((_, v1) in v) { val tvViewModel = TVViewModel(v1)
list2.add( tvViewModel.setRowPosition(idx.toInt())
Info( tvViewModel.setItemPosition(idx2)
idx.toInt(), idx2, v1 tvListViewModel.addTVViewModel(tvViewModel)
) listRowAdapter.add(tvViewModel)
)
listRowAdapter.add(v1)
idx2++
tvListViewModel.addTV(v1)
} }
tvListViewModel.maxNum.add(v.size)
val header = HeaderItem(idx, k) val header = HeaderItem(idx, k)
rowsAdapter!!.add(ListRow(header, listRowAdapter)) rowsAdapter!!.add(ListRow(header, listRowAdapter))
idx++ idx++
@@ -111,17 +173,14 @@ class MainFragment : BrowseSupportFragment() {
adapter = rowsAdapter adapter = rowsAdapter
itemPosition = sharedPref?.getInt("position", 0)!! itemPosition = sharedPref?.getInt("position", 0)!!
if (itemPosition >= tvListViewModel.size()) {
val tvModel = tvListViewModel.getTVModel(itemPosition) itemPosition = 0
if (tvModel?.ysp() != null) { savePosition(0)
lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) }
}
} else {
(activity as? MainActivity)?.play(list2[itemPosition].item)
// (activity as? MainActivity)?.switchInfoFragment(list2[itemPosition].item)
} }
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
@@ -132,6 +191,7 @@ class MainFragment : BrowseSupportFragment() {
} }
fun savePosition(position: Int) { fun savePosition(position: Int) {
tvListViewModel.setItemPosition(position)
with(sharedPref!!.edit()) { with(sharedPref!!.edit()) {
putInt("position", position) putInt("position", position)
apply() apply()
@@ -142,95 +202,63 @@ class MainFragment : BrowseSupportFragment() {
view?.post { view?.post {
itemPosition-- itemPosition--
if (itemPosition == -1) { if (itemPosition == -1) {
itemPosition = list2.size - 1 itemPosition = tvListViewModel.size() - 1
} }
savePosition(itemPosition) savePosition(itemPosition)
val l = list2[itemPosition] val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) {
lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) }
}
} else {
l.item.let { (activity as? MainActivity)?.play(it) }
}
setSelectedPosition(
l.rowPosition, false,
SelectItemViewHolderTask(l.itemPosition)
)
Toast.makeText(
activity,
l.item.title,
Toast.LENGTH_SHORT
).show()
} }
} }
fun next() { fun next() {
view?.post { view?.post {
itemPosition++ itemPosition++
if (itemPosition == list2.size) { if (itemPosition == tvListViewModel.size()) {
itemPosition = 0 itemPosition = 0
} }
savePosition(itemPosition) savePosition(itemPosition)
val l = list2[itemPosition] val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
val tvModel = tvListViewModel.getTVModel(itemPosition)
if (tvModel?.ysp() != null) {
lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) }
}
} else {
l.item.let { (activity as? MainActivity)?.play(it) }
}
setSelectedPosition(
l.rowPosition, false,
SelectItemViewHolderTask(l.itemPosition)
)
Toast.makeText(
activity,
l.item.title,
Toast.LENGTH_SHORT
).show()
} }
} }
fun prevSource() { fun prevSource() {
view?.post { view?.post {
val tv = list2[itemPosition].item val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tv.videoUrl.size > 1) { if (tvViewModel != null) {
tv.videoIndex-- if (tvViewModel.videoUrl.value!!.size > 1) {
if (tv.videoIndex == -1) { val videoIndex = tvViewModel.videoIndex.value?.minus(1)
tv.videoIndex = tv.videoUrl.size - 1 if (videoIndex == -1) {
tvViewModel.setVideoIndex(tvViewModel.videoUrl.value!!.size - 1)
}
tvViewModel.changed()
} }
(activity as? MainActivity)?.play(tv)
// (activity as? MainActivity)?.switchInfoFragment(tv)
} }
} }
} }
fun nextSource() { fun nextSource() {
view?.post { view?.post {
val tv = list2[itemPosition].item val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvViewModel != null) {
if (tv.videoUrl.size > 1) { tvViewModel.changed()
tv.videoIndex++ // if (tvViewModel.videoUrl.value!!.size > 1) {
if (tv.videoIndex == tv.videoUrl.size) { // val videoIndex = tvViewModel.videoIndex.value?.plus(1)
tv.videoIndex = 0 // if (videoIndex == tvViewModel.videoUrl.value!!.size) {
} // tvViewModel.setVideoIndex(0)
// }
(activity as? MainActivity)?.play(tv) // tvViewModel.changed()
// (activity as? MainActivity)?.switchInfoFragment(tv) // }
} }
} }
} }
fun tvViewModel(): TVViewModel? {
return tvListViewModel.getTVViewModel(itemPosition)
}
private fun setupEventListeners() { private fun setupEventListeners() {
onItemViewClickedListener = ItemViewClickedListener() onItemViewClickedListener = ItemViewClickedListener()
onItemViewSelectedListener = ItemViewSelectedListener() onItemViewSelectedListener = ItemViewSelectedListener()
@@ -243,19 +271,12 @@ class MainFragment : BrowseSupportFragment() {
rowViewHolder: RowPresenter.ViewHolder, rowViewHolder: RowPresenter.ViewHolder,
row: Row row: Row
) { ) {
if (item is TV) { if (item is TVViewModel) {
itemPosition = item.id itemPosition = item.id.value!!
savePosition(itemPosition) savePosition(itemPosition)
val tvModel = tvListViewModel.getTVModel(itemPosition) val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvModel?.ysp() != null) { tvViewModel?.changed()
lifecycleScope.launch(Dispatchers.IO) {
tvModel.let { request?.fetchData(it) }
}
} else {
(activity as? MainActivity)?.play(item)
// (activity as? MainActivity)?.switchInfoFragment(item)
}
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
@@ -269,13 +290,18 @@ class MainFragment : BrowseSupportFragment() {
) { ) {
if (itemViewHolder == null) { if (itemViewHolder == null) {
view?.post { view?.post {
val it = list2[itemPosition] val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
setSelectedPosition( if (tvViewModel != null) {
it.rowPosition, false, setSelectedPosition(
SelectItemViewHolderTask(it.itemPosition) tvViewModel.getRowPosition(), false,
) SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
}
} }
} }
if (item is TVViewModel) {
tvListViewModel.setItemPositionCurrent(item.id.value!!)
}
} }
} }
@@ -1,6 +1,7 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context import android.content.Context
import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
import androidx.leanback.media.MediaPlayerAdapter import androidx.leanback.media.MediaPlayerAdapter
@@ -17,6 +18,7 @@ class PlaybackControlGlue(
if (event!!.action == KeyEvent.ACTION_DOWN) { if (event!!.action == KeyEvent.ACTION_DOWN) {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_DPAD_CENTER -> { KeyEvent.KEYCODE_DPAD_CENTER -> {
Log.i(TAG, "KEYCODE_DPAD_CENTER")
(context as? MainActivity)?.switchMainFragment() (context as? MainActivity)?.switchMainFragment()
} }
@@ -50,6 +52,6 @@ class PlaybackControlGlue(
} }
companion object { companion object {
private const val TAG = "CustomPlaybackControlGlue" private const val TAG = "PlaybackControlGlue"
} }
} }
@@ -5,9 +5,10 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.leanback.app.VideoSupportFragment import androidx.leanback.app.VideoSupportFragment
import androidx.leanback.app.VideoSupportFragmentGlueHost import androidx.leanback.app.VideoSupportFragmentGlueHost
import androidx.leanback.media.PlayerAdapter
import androidx.leanback.media.PlaybackTransportControlGlue import androidx.leanback.media.PlaybackTransportControlGlue
import androidx.leanback.media.PlayerAdapter
import androidx.leanback.widget.PlaybackControlsRow import androidx.leanback.widget.PlaybackControlsRow
import com.lizongying.mytv.models.TVViewModel
import java.io.IOException import java.io.IOException
class PlaybackFragment : VideoSupportFragment() { class PlaybackFragment : VideoSupportFragment() {
@@ -29,12 +30,10 @@ class PlaybackFragment : VideoSupportFragment() {
override fun showControlsOverlay(runAnimation: Boolean) { override fun showControlsOverlay(runAnimation: Boolean) {
} }
fun play(tv: TV) { fun play(tvModel: TVViewModel) {
val videoUrl = tv.videoUrl[tv.videoIndex] val videoUrl = tvModel.videoIndex.value?.let { tvModel.videoUrl.value?.get(it) }
if (videoUrl == null || videoUrl == "") {
if (videoUrl == "") { Log.e(TAG, "${tvModel.title.value} videoUrl is empty")
Log.e(TAG, "tv $tv")
Log.e(TAG, "videoUrl is empty")
return return
} }
@@ -45,21 +44,31 @@ class PlaybackFragment : VideoSupportFragment() {
lastVideoUrl = videoUrl lastVideoUrl = videoUrl
playerAdapter?.reset()
val glueHost = VideoSupportFragmentGlueHost(this@PlaybackFragment) val glueHost = VideoSupportFragmentGlueHost(this@PlaybackFragment)
mTransportControlGlue = PlaybackControlGlue(activity, playerAdapter) mTransportControlGlue = PlaybackControlGlue(activity, playerAdapter)
mTransportControlGlue.host = glueHost mTransportControlGlue.host = glueHost
mTransportControlGlue.playWhenPrepared() mTransportControlGlue.playWhenPrepared()
playerAdapter?.callback = PlayerCallback(tvModel)
try { try {
playerAdapter?.setDataSource(Uri.parse(videoUrl)) playerAdapter?.setDataSource(Uri.parse(videoUrl))
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, "error $e")
return return
} }
hideControlsOverlay(false) hideControlsOverlay(false)
} }
private inner class PlayerCallback(private var tvModel: TVViewModel) :
PlayerAdapter.Callback() {
override fun onError(adapter: PlayerAdapter?, errorCode: Int, errorMessage: String?) {
Log.e(TAG, "on error: $errorMessage")
if (tvModel.ysp() != null && tvModel.videoIndex.value!! > 0 && errorMessage == "Source error") {
tvModel.changed()
}
}
}
companion object { companion object {
private const val TAG = "PlaybackVideoFragment" private const val TAG = "PlaybackVideoFragment"
} }
@@ -12,9 +12,13 @@ import com.lizongying.mytv.api.YSPApiService
import com.lizongying.mytv.api.YSPProtoService import com.lizongying.mytv.api.YSPProtoService
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
@@ -37,17 +41,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 "湖南卫视",
@@ -82,6 +86,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.title.value
val request = data?.let { LiveInfoRequest(it) } val request = data?.let { LiveInfoRequest(it) }
request?.let { yspApiService?.getLiveInfo(it) } request?.let { yspApiService?.getLiveInfo(it) }
@@ -98,25 +103,34 @@ class Request(var context: Context) {
val decodedString = String(decodedBytes) val decodedString = String(decodedBytes)
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""") val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val matchResult = regex.find(decodedString) val matchResult = regex.find(decodedString)
var keyBytes = byteArrayOf()
var ivBytes = byteArrayOf()
if (matchResult != null) { if (matchResult != null) {
val (key, iv) = matchResult.destructured val (key, iv) = matchResult.destructured
keyBytes = Base64.decode(key, Base64.DEFAULT) val keyBytes = Base64.decode(key, Base64.DEFAULT)
ivBytes = Base64.decode(iv, Base64.DEFAULT) val ivBytes = Base64.decode(iv, Base64.DEFAULT)
} val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
tvModel.updateVideoUrlByYSP(
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()
) Log.i(TAG, "$title url $url")
tvModel.addVideoUrl(url)
tvModel.allReady()
} 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, "$title request error")
tvModel.firstSource()
} }
}) })
} }
@@ -146,7 +160,8 @@ class Request(var context: Context) {
if (!mapping.containsKey(item.channelName)) { if (!mapping.containsKey(item.channelName)) {
continue continue
} }
val tv = TVList.list[channelType]?.get(mapping[item.channelName]) val tv =
TVList.list[channelType]?.find { it.title == mapping[item.channelName] }
if (tv != null) { if (tv != null) {
tv.logo = item.tvLogo tv.logo = item.tvLogo
tv.pid = item.pid tv.pid = item.pid
@@ -163,22 +178,35 @@ class Request(var context: Context) {
}) })
} }
fun fetchProgram(tvModel: TVViewModel) { private fun getCurrentDate(): String {
yspProtoService?.getProgram("", "")?.enqueue(object : Callback<LiveInfo> { val currentDate = Date()
override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) { val formatter = SimpleDateFormat("yyyyMMdd", Locale.CHINA)
if (response.isSuccessful) { return formatter.format(currentDate)
val liveInfo = response.body() }
Log.i(TAG, "${liveInfo?.data?.playurl}")
}
}
override fun onFailure(call: Call<LiveInfo>, t: Throwable) { fun fetchProgram(tvModel: TVViewModel) {
} yspProtoService?.getProgram(tvModel.programId.value!!, getCurrentDate())
}) ?.enqueue(object : Callback<epgProgramModel.Response> {
override fun onResponse(
call: Call<epgProgramModel.Response>,
response: Response<epgProgramModel.Response>
) {
if (response.isSuccessful) {
val program = response.body()
if (program != null) {
tvModel.addProgram(program.dataListList)
}
}
}
override fun onFailure(call: Call<epgProgramModel.Response>, t: Throwable) {
Log.e(TAG, "Program request failed", t)
}
})
} }
private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String { private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String {
var plaintext = val plaintext =
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,AppleM1Pro,OpenGL4.1)"}""" """{"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")
+24 -30
View File
@@ -1,50 +1,45 @@
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, List<TV>> by lazy {
setupTV() setupTV()
} }
private var count: Int = 0 private var count: Int = 0
private fun setupTV(): Map<String, Map<String, TV>> { private fun setupTV(): Map<String, List<TV>> {
val tvs = """ val tvs = """
央视频道 央视频道
CCTV4K,,https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp,600002264,2000266303 CCTV4K,,https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp,600002264,2000266303
CCTV1 综合,http://tvpull.dxhmt.cn/tv/11481-4.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp,600001859,2000210103 CCTV1 综合,http://tvpull.dxhmt.cn/tv/11481-4.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp,600001859,2000210103
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,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
@@ -148,7 +143,6 @@ CETV4,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226225/index.m3u8
兵团卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226214/index.m3u8 兵团卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226214/index.m3u8
延边卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226220/index.m3u8 延边卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226220/index.m3u8
内蒙古卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225786/index.m3u8 内蒙古卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225786/index.m3u8
康巴卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226234/index.m3u8
电视指南,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8 电视指南,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8
风云足球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8 风云足球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8
风云剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8 风云剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8
@@ -211,7 +205,7 @@ CHC动作电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226465/in
老故事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226236/index.m3u8 老故事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226236/index.m3u8
""".trimIndent() """.trimIndent()
val map: MutableMap<String, MutableMap<String, TV>> = mutableMapOf() val map: MutableMap<String, MutableList<TV>> = mutableMapOf()
var channel = "" var channel = ""
for (i in tvs.split("\n")) { for (i in tvs.split("\n")) {
@@ -223,14 +217,13 @@ CHC动作电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226465/in
continue continue
} }
val p = i.split(",") val p = i.split(",")
val titleMap = map[channel] ?: mutableMapOf() val titleMap = map[channel] ?: mutableListOf()
val tv = titleMap[p[0]] ?: TV( val tv = TV(
count, count,
p[0], p[0],
p[1].split(";").map { it.trim() } p[1].split(";").map { it.trim() }
) )
count++
if (p.size > 2) { if (p.size > 2) {
tv.logo = p[2] tv.logo = p[2]
@@ -242,8 +235,9 @@ CHC动作电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226465/in
tv.sid = p[4] tv.sid = p[4]
} }
titleMap[p[0]] = tv titleMap.add(tv)
map[channel] = titleMap map[channel] = titleMap
count++
} }
return map return map
@@ -1,24 +0,0 @@
package com.lizongying.mytv.api
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.IOException
data class Page(
val code: Int,
val msg: String,
val data: Data,
)
data class Data(
val feedModuleList: List<Module>
)
data class Module(
val dataTvChannelList: List<TvChannelListData>
)
data class TvChannelListData(
val dataTvChannelList: List<TvChannelListData>
)
@@ -64,8 +64,8 @@ class YSP(var context: Context) {
} }
fun switch(tvModel: TVViewModel): String { fun switch(tvModel: TVViewModel): String {
livepid = tvModel.getTV().pid livepid = tvModel.pid.value!!
cnlid = tvModel.getTV().sid cnlid = tvModel.sid.value!!
defn = "fhd" defn = "fhd"
// guid = getGuid() // guid = getGuid()
@@ -99,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,7 @@
package com.lizongying.mytv.api package com.lizongying.mytv.api
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
import retrofit2.Call import retrofit2.Call
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Headers
@@ -15,13 +16,13 @@ interface YSPProtoService {
fun getPage( fun getPage(
): Call<pageModel.Response> ): Call<pageModel.Response>
@GET("api/yspepg/program/{livepid}/{cnlid}") @GET("api/yspepg/program/{livepid}/{date}")
@Headers( @Headers(
"yspappid: 519748109", "yspappid: 519748109",
"platform: 109", "platform: 109",
) )
fun getProgram( fun getProgram(
@Path("livepid") livepid: String, @Path("livepid") livepid: String,
@Path("cnlid") cnlid: String, @Path("date") date: String,
): Call<LiveInfo> ): Call<epgProgramModel.Response>
} }
@@ -1,44 +1,61 @@
package com.lizongying.mytv.models package com.lizongying.mytv.models
import android.util.Log import android.content.SharedPreferences
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.lizongying.mytv.TV
class TVListViewModel : ViewModel() { class TVListViewModel : ViewModel() {
private val tvListLiveData = MutableLiveData<MutableList<TV>>()
private val tvModelListLiveData = MutableLiveData<MutableList<TVViewModel>>() var maxNum = mutableListOf<Int>()
fun getListLiveData(): MutableLiveData<MutableList<TVViewModel>> { private var sharedPref: SharedPreferences? = null
return tvModelListLiveData
private val tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
private val _itemPosition = MutableLiveData<Int>()
val itemPosition: LiveData<Int>
get() = _itemPosition
private val _itemPositionCurrent = MutableLiveData<Int>()
val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent
fun getTVListViewModel(): MutableLiveData<MutableList<TVViewModel>> {
return tvListViewModel
} }
fun addTV(tv: TV) { fun addTVViewModel(tvViewModel: TVViewModel) {
val currentList = tvListLiveData.value ?: mutableListOf() val currentTVModelList = tvListViewModel.value ?: mutableListOf()
currentList.add(tv) currentTVModelList.add(tvViewModel)
tvListLiveData.value = currentList tvListViewModel.value = currentTVModelList
val currentModelList = tvModelListLiveData.value ?: mutableListOf()
currentModelList.add(TVViewModel(tv))
tvModelListLiveData.value = currentModelList
} }
fun updateTV(tv: TV) { fun getTVViewModel(id: Int): TVViewModel? {
val currentList = tvListLiveData.value ?: mutableListOf() return tvListViewModel.value?.get(id)
currentList[tv.id] = tv
tvListLiveData.value = currentList
val currentModelList = tvModelListLiveData.value ?: mutableListOf()
currentModelList[tv.id].update(tv)
tvModelListLiveData.value = currentModelList
} }
fun getTV(id: Int): TV? { fun getTVViewModelCurrent(): TVViewModel? {
return tvListLiveData.value?.get(id) return _itemPositionCurrent.value?.let { tvListViewModel.value?.get(it) }
} }
fun getTVModel(id: Int): TVViewModel? { fun setItemPosition(position: Int) {
return tvModelListLiveData.value?.get(id) _itemPosition.value = position
_itemPositionCurrent.value = position
}
fun setItemPositionCurrent(position: Int) {
_itemPositionCurrent.value = position
}
fun savePosition(position: Int) {
with(sharedPref!!.edit()) {
putInt("position", position)
apply()
}
}
fun size(): Int {
return tvListViewModel.value!!.size
} }
} }
@@ -1,14 +1,17 @@
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
import com.lizongying.mytv.TV import com.lizongying.mytv.TV
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
import java.util.Date
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 +21,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 +56,174 @@ 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 mappingLogo = mapOf(
"CCTV4K" to "https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp",
"CCTV1" to "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
"CCTV1 综合" to "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
"CCTV2" to "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
"CCTV2 财经" to "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
"CCTV3" to "https://p2.img.cctvpic.com/photoAlbum/page/performance/img/2021/8/16/1629103576424_839.png",
"CCTV3 综艺" to "https://p2.img.cctvpic.com/photoAlbum/page/performance/img/2021/8/16/1629103576424_839.png",
"CCTV4" to "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
"CCTV4 中文国际" to "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
"CCTV5" to "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
"CCTV5 体育" to "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
"CCTV5+" to "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
"CCTV5+ 体育赛事" to "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
"CCTV6" to "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
"CCTV6 电影" to "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
"CCTV7" to "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
"CCTV7 国防军事" to "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
"CCTV8" to "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
"CCTV8 电视剧" to "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
"CCTV9" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
"CCTV9 记录" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
"CCTV10" to "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
"CCTV10 科教" to "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
"CCTV11" to "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
"CCTV11 戏曲" to "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
"CCTV12" to "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
"CCTV12 社会与法" to "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
"CCTV13" to "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
"CCTV13 新闻" to "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
"CCTV14" to "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
"CCTV14 少儿" to "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
"CCTV15" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
"CCTV15 音乐" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
"CCTV16" to "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
"CCTV16 奥林匹克" to "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
"CCTV17" to "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
"CCTV17 农业农村" to "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
"CGTN" to "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
"CGTN 记录频道" to "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
"CGTN 法语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
"CGTN 俄语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
"CGTN 西班牙语频道" to "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
"CGTN 阿拉伯语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
"东方卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
"湖南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
"湖北卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
"辽宁卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
"江苏卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
"江西卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
"山东卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
"广东卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
"广西卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
"重庆卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/657651f411de2673d1770d9a78b44c1265704f7468cc41d4be7f51d630768494.png?imageMogr2/format/webp",
"河南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
"河北卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
"贵州卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
"北京卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
"黑龙江卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
"浙江卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
"安徽卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
"深圳卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
"四川卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
"东南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
"海南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
)
private var mappingEPG = mapOf(
"CCTV4K" to "600002264",
"CCTV1" to "600001859",
"CCTV1 综合" to "600001859",
"CCTV2" to "600001800",
"CCTV2 财经" to "600001800",
// "CCTV3" to "",
// "CCTV3 综艺" to "",
"CCTV4" to "600001814",
"CCTV4 中文国际" to "600001814",
"CCTV5" to "600001818",
"CCTV5 体育" to "600001818",
"CCTV5+" to "600001817",
"CCTV5+ 体育赛事" to "600001817",
"CCTV6" to "600001802",
"CCTV6 电影" to "600001802",
"CCTV7" to "600004092",
"CCTV7 国防军事" to "600004092",
"CCTV8" to "600001803",
"CCTV8 电视剧" to "600001803",
"CCTV9" to "600004078",
"CCTV9 记录" to "600004078",
"CCTV10" to "600001805",
"CCTV10 科教" to "600001805",
"CCTV11" to "600001806",
"CCTV11 戏曲" to "600001806",
"CCTV12" to "600001807",
"CCTV12 社会与法" to "600001807",
"CCTV13" to "600001811",
"CCTV13 新闻" to "600001811",
"CCTV14" to "600001809",
"CCTV14 少儿" to "600001809",
"CCTV15" to "600001815",
"CCTV15 音乐" to "600001815",
"CCTV16" to "600098637",
"CCTV16 奥林匹克" to "600098637",
"CCTV17" to "600001810",
"CCTV17 农业农村" to "600001810",
"CGTN" to "600014550",
"CGTN 法语频道" to "600084704",
"CGTN 俄语频道" to "600084758",
"CGTN 西班牙语频道" to "600084744",
"CGTN 阿拉伯语频道" to "600084782",
"东方卫视" to "600002483",
"湖南卫视" to "600002475",
"湖北卫视" to "600002508",
"辽宁卫视" to "600002505",
"江苏卫视" to "600002521",
"江西卫视" to "600002503",
"山东卫视" to "600002513",
"广东卫视" to "600002485",
"广西卫视" to "600002509",
"重庆卫视" to "600002531",
"河南卫视" to "600002525",
"河北卫视" to "600002493",
"贵州卫视" to "600002490",
"北京卫视" to "600002309",
"黑龙江卫视" to "600002498",
"浙江卫视" to "600002520",
"安徽卫视" to "600002532",
"深圳卫视" to "600002481",
"四川卫视" to "600002516",
"东南卫视" to "600002484",
"海南卫视" to "600002506",
)
private var isFirstTime = true
private var rowPosition: Int = 0
private var itemPosition: Int = 0
private val _programId = MutableLiveData<String>()
val programId: LiveData<String>
get() = _programId
private var _program = MutableLiveData<MutableList<Program>>()
val program: LiveData<MutableList<Program>>
get() = _program
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 _logo = MutableLiveData<String>()
val logo: LiveData<String>
get() = _logo
private val _pid = MutableLiveData<String>() private val _pid = MutableLiveData<String>()
val pid: LiveData<String> val pid: LiveData<String>
get() = _pid get() = _pid
@@ -67,37 +232,93 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val sid: LiveData<String> val sid: LiveData<String>
get() = _sid get() = _sid
private val _backgroundImage = MutableLiveData<String>() private val _change = MutableLiveData<Boolean>()
val backgroundImage: LiveData<String> val change: LiveData<Boolean>
get() = _backgroundImage get() = _change
fun getBackgroundImage(): String { private val _ready = MutableLiveData<Boolean>()
return tv.logo ?: "" val ready: LiveData<Boolean>
get() = _ready
fun addVideoUrl(url: String) {
if (_videoUrl.value?.isNotEmpty() == true) {
if (_videoUrl.value!!.last().contains("cctv.cn")) {
tv.videoUrl = tv.videoUrl.subList(0, tv.videoUrl.lastIndex) + listOf(url)
} else {
tv.videoUrl = tv.videoUrl + listOf(url)
}
} else {
tv.videoUrl = tv.videoUrl + listOf(url)
}
tv.videoIndex = tv.videoUrl.lastIndex
_videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoIndex
} }
fun updateBackgroundImage(url: String) { fun firstSource() {
_backgroundImage.value = url if (_videoUrl.value!!.isNotEmpty()) {
setVideoIndex(0)
allReady()
} else {
Log.e(TAG, "no first")
}
} }
private fun updateVideoUrl(url: String) { fun changed() {
tv.videoUrl = listOf(url) _change.value = true
tv.videoIndex = 0
_videoUrl.value = listOf(url)
} }
fun updateVideoUrlByYSP(url: String) { fun allReady() {
updateByYSP = true _ready.value = true
updateVideoUrl(url) }
fun setVideoIndex(videoIndex: Int) {
_videoIndex.value = videoIndex
}
fun setLogo(url: String) {
_logo.value = url
} }
init { init {
_id.value = tv.id
_title.value = tv.title
_videoUrl.value = tv.videoUrl _videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoIndex
if (mappingLogo[tv.title] != null) {
_logo.value = mappingLogo[tv.title]
}
_pid.value = tv.pid _pid.value = tv.pid
_sid.value = tv.sid _sid.value = tv.sid
_program.value = mutableListOf()
if (mappingEPG[tv.title] != null) {
_programId.value = mappingEPG[tv.title]
}
} }
fun updateByYSP(): Boolean { fun getIsFirstTime(): Boolean {
return updateByYSP return isFirstTime
}
fun isFirstTime(firstTime: Boolean) {
isFirstTime = firstTime
}
fun getRowPosition(): Int {
return rowPosition
}
fun getItemPosition(): Int {
return itemPosition
}
fun setRowPosition(position: Int) {
rowPosition = position
}
fun setItemPosition(position: Int) {
itemPosition = position
} }
fun update(t: TV) { fun update(t: TV) {
@@ -114,4 +335,33 @@ class TVViewModel(private var tv: TV) : ViewModel() {
} }
return mapping[tv.title] return mapping[tv.title]
} }
fun getProgram(): MutableList<Program>? {
_program.value = (_program.value?.filter { it.et > (Date().time / 1000) })?.toMutableList()
return _program.value?.subList(0, 2)
}
fun getProgramOne(): Program? {
val programNew = (_program.value?.filter { it.et > (Date().time / 1000) })?.toMutableList()
if (_program.value != programNew) {
_program.value = programNew
}
if (_program.value!!.isEmpty()) {
return null
}
return _program.value?.first()
}
fun addProgram(p: MutableList<Program>) {
if (_program.value == null) {
_program.value = p
} else {
_program.value =
((_program.value?.filter { it.st < p.first().st })?.plus(p))?.toMutableList()
}
}
companion object {
private const val TAG = "TVViewModel"
}
} }
+2 -1
View File
@@ -2,7 +2,8 @@
<style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle"> <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
<item name="lbImageCardViewType">Title</item> <!-- <item name="infoVisibility">activated</item>-->
<item name="lbImageCardViewType">Title|Content</item>
</style> </style>
<style name="CustomImageCardTheme" parent="Theme.Leanback"> <style name="CustomImageCardTheme" parent="Theme.Leanback">
+6 -1
View File
@@ -1,4 +1,9 @@
<resources> <resources>
<style name="Theme.MyTV" parent="@style/Theme.Leanback" /> <style name="Theme.MyTV" parent="@style/Theme.Leanback">
<item name="browsePaddingStart">11dp</item>
<item name="browsePaddingEnd">11dp</item>
<item name="browseRowsMarginTop">54dp</item>
</style>
</resources> </resources>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB