mirror of
https://github.com/lizongying/my-tv.git
synced 2026-10-26 22:26:29 +08:00
init
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.util.Log
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.leanback.widget.ImageCardView
|
||||
import androidx.leanback.widget.Presenter
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
||||
/**
|
||||
* A CardPresenter is used to generate Views and bind Objects to them on demand.
|
||||
* It contains an ImageCardView.
|
||||
*/
|
||||
class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Presenter() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
||||
Log.d(TAG, "onCreateViewHolder")
|
||||
|
||||
val cardView = object :
|
||||
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {
|
||||
override fun setSelected(selected: Boolean) {
|
||||
// updateCardBackgroundColor(this)
|
||||
super.setSelected(selected)
|
||||
}
|
||||
}
|
||||
|
||||
cardView.isFocusable = true
|
||||
cardView.isFocusableInTouchMode = true
|
||||
return ViewHolder(cardView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
|
||||
val tv = item as TV
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
|
||||
Log.d(TAG, "onBindViewHolder")
|
||||
if (tv.videoUrl != null) {
|
||||
cardView.titleText = tv.title
|
||||
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
|
||||
cardView.tag = tv.videoUrl
|
||||
|
||||
// lifecycleScope.launch(Dispatchers.IO) {
|
||||
// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) }
|
||||
//
|
||||
// withContext(Dispatchers.Main) {
|
||||
// cardView.mainImageView.setImageBitmap(videoThumbnail)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
|
||||
Log.d(TAG, "onUnbindViewHolder")
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
// Remove references to images so that the garbage collector can free up memory
|
||||
cardView.badgeImage = null
|
||||
cardView.mainImage = null
|
||||
}
|
||||
|
||||
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? {
|
||||
val mediaMetadataRetriever = MediaMetadataRetriever()
|
||||
try {
|
||||
val map = HashMap<String, String>()
|
||||
mediaMetadataRetriever.setDataSource(url, map)
|
||||
return mediaMetadataRetriever.frameAtTime
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
mediaMetadataRetriever.release()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CardPresenter"
|
||||
|
||||
private const val CARD_WIDTH = 313
|
||||
|
||||
private const val CARD_HEIGHT = 176
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
|
||||
data class Info(
|
||||
var rowPosition: Int = 0,
|
||||
var itemPosition: Int = 0,
|
||||
var item: TV? = null,
|
||||
) : Serializable
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
||||
/**
|
||||
* Loads [MainFragment].
|
||||
*/
|
||||
class MainActivity : FragmentActivity() {
|
||||
|
||||
private val playbackFragment = PlaybackFragment()
|
||||
private val mainFragment = MainFragment()
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var hideTask: Runnable? = null
|
||||
|
||||
private var doubleBackToExitPressedOnce = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.main_browse_fragment, playbackFragment)
|
||||
.add(R.id.main_browse_fragment, mainFragment)
|
||||
.commit()
|
||||
|
||||
hideTask = Runnable {
|
||||
Log.i(TAG, "hideTask")
|
||||
hideMainFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun play(tv: TV) {
|
||||
playbackFragment.play(tv)
|
||||
}
|
||||
|
||||
fun prev() {
|
||||
Log.i(TAG, "prev")
|
||||
mainFragment.prev()
|
||||
}
|
||||
|
||||
fun next() {
|
||||
Log.i(TAG, "next")
|
||||
mainFragment.next()
|
||||
}
|
||||
|
||||
fun switchMainFragment() {
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
|
||||
if (mainFragment.isHidden) {
|
||||
focusMainFragment()
|
||||
transaction.show(mainFragment)
|
||||
} else {
|
||||
transaction.hide(mainFragment)
|
||||
}
|
||||
|
||||
transaction.commit()
|
||||
}
|
||||
|
||||
fun focusMainFragment() {
|
||||
mainFragment.focus()
|
||||
}
|
||||
|
||||
fun fragmentIsHidden(): Boolean {
|
||||
return mainFragment.isHidden
|
||||
}
|
||||
|
||||
fun hideMainFragment() {
|
||||
if (!mainFragment.isHidden) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.hide(mainFragment)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
fun startHideTask(delayMillis: Long) {
|
||||
hideTask?.let { handler.postDelayed(it, delayMillis) }
|
||||
}
|
||||
|
||||
fun cancelHideTask() {
|
||||
hideTask?.let { handler.removeCallbacks(it) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cancelHideTask()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK -> {
|
||||
if (doubleBackToExitPressedOnce) {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
hideMainFragment()
|
||||
this.doubleBackToExitPressedOnce = true
|
||||
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show()
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
doubleBackToExitPressedOnce = false
|
||||
}, 2000)
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_MENU -> {
|
||||
val imageView = ImageView(this)
|
||||
val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate)
|
||||
imageView.setImageDrawable(drawable)
|
||||
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
builder
|
||||
.setMessage("地址: https://github.com/lizongying/my-tv/")
|
||||
.setView(imageView)
|
||||
|
||||
val dialog: AlertDialog = builder.create()
|
||||
dialog.show()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
switchMainFragment()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
if (mainFragment.isHidden) {
|
||||
prev()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
if (mainFragment.isHidden) {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
if (mainFragment.isHidden) {
|
||||
prev()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
if (mainFragment.isHidden) {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.leanback.app.BrowseSupportFragment
|
||||
import androidx.leanback.widget.ArrayObjectAdapter
|
||||
import androidx.leanback.widget.HeaderItem
|
||||
import androidx.leanback.widget.ListRow
|
||||
import androidx.leanback.widget.ListRowPresenter
|
||||
import androidx.leanback.widget.ListRowPresenter.SelectItemViewHolderTask
|
||||
import androidx.leanback.widget.OnItemViewClickedListener
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener
|
||||
import androidx.leanback.widget.Presenter
|
||||
import androidx.leanback.widget.Row
|
||||
import androidx.leanback.widget.RowPresenter
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
||||
/**
|
||||
* Loads a grid of cards with movies to browse.
|
||||
*/
|
||||
class MainFragment : BrowseSupportFragment() {
|
||||
|
||||
var itemPosition: Int = 0
|
||||
|
||||
private val list2: MutableList<Info> = mutableListOf()
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
Log.i(TAG, "onCreate")
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
setupUIElements()
|
||||
|
||||
loadRows()
|
||||
|
||||
setupEventListeners()
|
||||
}
|
||||
|
||||
fun show() {
|
||||
if (!view?.isVisible!!) {
|
||||
view?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUIElements() {
|
||||
// set fastLane (or headers) background color
|
||||
brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background)
|
||||
// headersState = HEADERS_DISABLED
|
||||
}
|
||||
|
||||
private fun loadRows() {
|
||||
val list = TVList.list
|
||||
val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
|
||||
val cardPresenter = CardPresenter(lifecycleScope)
|
||||
|
||||
var idx: Long = 0
|
||||
for ((k, v) in list) {
|
||||
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
|
||||
for ((idx2, v1) in v.withIndex()) {
|
||||
listRowAdapter.add(v1)
|
||||
list2.add(Info(idx.toInt(), idx2, v1))
|
||||
}
|
||||
val header = HeaderItem(idx, k)
|
||||
rowsAdapter.add(ListRow(header, listRowAdapter))
|
||||
idx++
|
||||
}
|
||||
|
||||
adapter = rowsAdapter
|
||||
|
||||
(activity as? MainActivity)?.play(list.values.first()[0])
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
|
||||
fun focus() {
|
||||
if (!view?.isFocused!!) {
|
||||
view?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
fun prev() {
|
||||
view?.post {
|
||||
itemPosition--
|
||||
if (itemPosition == -1) {
|
||||
itemPosition = list2.size - 1
|
||||
}
|
||||
|
||||
val l = list2[itemPosition]
|
||||
l.item?.let { (activity as? MainActivity)?.play(it) }
|
||||
setSelectedPosition(
|
||||
l.rowPosition, false,
|
||||
SelectItemViewHolderTask(l.itemPosition)
|
||||
)
|
||||
// Toast.makeText(
|
||||
// activity,
|
||||
// "${l.title} $selectedPosition $itemPosition",
|
||||
// Toast.LENGTH_SHORT
|
||||
// ).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun next() {
|
||||
view?.post {
|
||||
itemPosition++
|
||||
if (itemPosition == list2.size) {
|
||||
itemPosition = 0
|
||||
}
|
||||
|
||||
val l = list2[itemPosition]
|
||||
l.item?.let { (activity as? MainActivity)?.play(it) }
|
||||
setSelectedPosition(
|
||||
l.rowPosition, false,
|
||||
SelectItemViewHolderTask(l.itemPosition)
|
||||
)
|
||||
// Toast.makeText(
|
||||
// activity,
|
||||
// "${l.title} $selectedPosition $itemPosition",
|
||||
// Toast.LENGTH_SHORT
|
||||
// ).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEventListeners() {
|
||||
onItemViewClickedListener = ItemViewClickedListener()
|
||||
onItemViewSelectedListener = ItemViewSelectedListener()
|
||||
}
|
||||
|
||||
private inner class ItemViewClickedListener : OnItemViewClickedListener {
|
||||
override fun onItemClicked(
|
||||
itemViewHolder: Presenter.ViewHolder,
|
||||
item: Any,
|
||||
rowViewHolder: RowPresenter.ViewHolder,
|
||||
row: Row
|
||||
) {
|
||||
Log.d(TAG, "onItemClicked")
|
||||
if (item is TV) {
|
||||
Log.d(TAG, "Item: $item")
|
||||
(activity as? MainActivity)?.play(item)
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
itemPosition = item.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ItemViewSelectedListener : OnItemViewSelectedListener {
|
||||
override fun onItemSelected(
|
||||
itemViewHolder: Presenter.ViewHolder?, item: Any?,
|
||||
rowViewHolder: RowPresenter.ViewHolder, row: Row
|
||||
) {
|
||||
if (item is TV) {
|
||||
Log.i(TAG, "Item: ${item.id}")
|
||||
|
||||
}
|
||||
if (itemViewHolder == null) {
|
||||
view?.post {
|
||||
val l = list2[itemPosition]
|
||||
Log.i(TAG, "$l")
|
||||
setSelectedPosition(
|
||||
l.rowPosition, false,
|
||||
SelectItemViewHolderTask(l.itemPosition)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.leanback.media.MediaPlayerAdapter
|
||||
import androidx.leanback.media.PlaybackTransportControlGlue
|
||||
|
||||
class PlaybackControlGlue(
|
||||
context: Context?,
|
||||
playerAdapter: MediaPlayerAdapter?,
|
||||
) :
|
||||
PlaybackTransportControlGlue<MediaPlayerAdapter>(context, playerAdapter) {
|
||||
|
||||
override fun onKey(v: View?, keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (event!!.action == KeyEvent.ACTION_DOWN) {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
(context as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
||||
(context as? MainActivity)?.prev()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
||||
(context as? MainActivity)?.next()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
||||
(context as? MainActivity)?.prev()
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
||||
(context as? MainActivity)?.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onKey(v, keyCode, event)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CustomPlaybackControlGlue"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.leanback.app.VideoSupportFragment
|
||||
import androidx.leanback.app.VideoSupportFragmentGlueHost
|
||||
import androidx.leanback.media.MediaPlayerAdapter
|
||||
import androidx.leanback.media.PlaybackTransportControlGlue
|
||||
import androidx.leanback.widget.PlaybackControlsRow
|
||||
import java.io.IOException
|
||||
|
||||
/** Handles video playback with media controls. */
|
||||
class PlaybackFragment : VideoSupportFragment() {
|
||||
|
||||
private lateinit var mTransportControlGlue: PlaybackTransportControlGlue<MediaPlayerAdapter>
|
||||
private var playerAdapter: MediaPlayerAdapter? = null
|
||||
private var lastVideoUrl: String = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
playerAdapter = MediaPlayerAdapter(context)
|
||||
playerAdapter?.setRepeatAction(PlaybackControlsRow.RepeatAction.INDEX_NONE)
|
||||
|
||||
view?.isFocusable = false
|
||||
view?.isFocusableInTouchMode = false
|
||||
}
|
||||
|
||||
override fun showControlsOverlay(runAnimation: Boolean) {
|
||||
// We will do nothing here, and thus controls will never be shown
|
||||
}
|
||||
|
||||
fun play(tv: TV) {
|
||||
if (tv.videoUrl.isNullOrBlank()) {
|
||||
Log.e(TAG, "videoUrl is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if (tv.videoUrl.equals(lastVideoUrl)) {
|
||||
Log.e(TAG, "videoUrl is duplication")
|
||||
return
|
||||
}
|
||||
|
||||
lastVideoUrl = tv.videoUrl!!
|
||||
|
||||
playerAdapter?.reset()
|
||||
|
||||
val glueHost = VideoSupportFragmentGlueHost(this@PlaybackFragment)
|
||||
mTransportControlGlue = PlaybackControlGlue(activity, playerAdapter)
|
||||
mTransportControlGlue.host = glueHost
|
||||
mTransportControlGlue.playWhenPrepared()
|
||||
|
||||
try {
|
||||
playerAdapter?.setDataSource(Uri.parse(tv.videoUrl))
|
||||
} catch (e: IOException) {
|
||||
// Handle the exception
|
||||
return
|
||||
}
|
||||
hideControlsOverlay(false)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (playerAdapter?.mediaPlayer != null) {
|
||||
playerAdapter?.release()
|
||||
Log.d(TAG, "playerAdapter released")
|
||||
}
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PlaybackVideoFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Movie class represents video entity with title, description, image thumbs and video url.
|
||||
*/
|
||||
data class TV(
|
||||
var id: Int = 0,
|
||||
var title: String? = null,
|
||||
var videoUrl: String? = null,
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "TV{" +
|
||||
"id=" + id +
|
||||
", title='" + title + '\'' +
|
||||
", videoUrl='" + videoUrl + '\'' +
|
||||
'}'
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val serialVersionUID = 727566175075960653L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
object TVList {
|
||||
val list: Map<String, List<TV>> by lazy {
|
||||
setupTV()
|
||||
}
|
||||
private var count: Int = 0
|
||||
|
||||
private fun setupTV(): Map<String, List<TV>> {
|
||||
val tv = arrayOf(
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV1",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV2",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV3",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV4",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV4美洲",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV4欧洲",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV5",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频道",
|
||||
"CCTV5+",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV1",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV2",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV3",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV4",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV4美洲",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV4欧洲",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV5",
|
||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=",
|
||||
),
|
||||
arrayOf(
|
||||
"央视频",
|
||||
"CCTV5+",
|
||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=",
|
||||
),
|
||||
)
|
||||
|
||||
val map: MutableMap<String, MutableList<TV>> = mutableMapOf()
|
||||
|
||||
for (i in tv) {
|
||||
val channelName = i[0]
|
||||
val movieList = map[channelName] ?: mutableListOf()
|
||||
movieList.add(buildTV(i[1], i[2]))
|
||||
map[channelName] = movieList
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
private fun buildTV(
|
||||
title: String,
|
||||
videoUrl: String,
|
||||
): TV {
|
||||
val tv = TV()
|
||||
tv.id = count++
|
||||
tv.title = title
|
||||
tv.videoUrl = videoUrl
|
||||
return tv
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user