Compare commits

...
4 Commits
Author SHA1 Message Date
Li ZongYing a340ab73b1 change dialog 2023-12-07 11:19:22 +08:00
Li ZongYing bc5a0cad71 fix exit on control 2023-12-07 09:47:31 +08:00
Li ZongYing 709cc2ccdf fix empty string bug 2023-12-07 01:05:26 +08:00
Li ZongYing 3b01debf7f switch source 2023-12-06 23:51:17 +08:00
10 changed files with 144 additions and 108 deletions
+1 -1
View File
@@ -52,5 +52,5 @@ jobs:
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.sign_app.outputs.signedReleaseFile }} asset_path: ${{ steps.sign_app.outputs.signedReleaseFile }}
asset_name: my-tv.apk asset_name: my-tv-${{ github.ref_name }}.apk
asset_content_type: application/vnd.android.package-archive asset_content_type: application/vnd.android.package-archive
+1 -1
View File
@@ -46,7 +46,7 @@ static def VersionName() {
try { try {
def process = 'git describe --tags --always'.execute() def process = 'git describe --tags --always'.execute()
process.waitFor() process.waitFor()
return process.text.trim() return process.text.trim() - "v"
} catch (ignored) { } catch (ignored) {
return "1.0.0" return "1.0.0"
} }
@@ -2,6 +2,7 @@ package com.lizongying.mytv
import android.graphics.Bitmap import android.graphics.Bitmap
import android.media.MediaMetadataRetriever 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 androidx.leanback.widget.ImageCardView import androidx.leanback.widget.ImageCardView
@@ -32,10 +33,9 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese
val tv = item as TV val tv = item as TV
val cardView = viewHolder.view as ImageCardView val cardView = viewHolder.view as ImageCardView
if (tv.videoUrl != null) { cardView.titleText = tv.title
cardView.titleText = tv.title cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) cardView.tag = tv.videoUrl
cardView.tag = tv.videoUrl
// lifecycleScope.launch(Dispatchers.IO) { // lifecycleScope.launch(Dispatchers.IO) {
// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) } // val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) }
@@ -44,7 +44,6 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese
// cardView.mainImageView.setImageBitmap(videoThumbnail) // cardView.mainImageView.setImageBitmap(videoThumbnail)
// } // }
// } // }
}
} }
override fun onUnbindViewHolder(viewHolder: ViewHolder) { override fun onUnbindViewHolder(viewHolder: ViewHolder) {
@@ -5,5 +5,5 @@ import java.io.Serializable
data class Info( data class Info(
var rowPosition: Int = 0, var rowPosition: Int = 0,
var itemPosition: Int = 0, var itemPosition: Int = 0,
var item: TV? = null, var item: TV,
) : Serializable ) : Serializable
@@ -4,11 +4,10 @@ import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.Gravity
import android.view.KeyEvent import android.view.KeyEvent
import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
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
@@ -44,6 +43,14 @@ class MainActivity : FragmentActivity() {
mainFragment.next() mainFragment.next()
} }
fun prevSource() {
mainFragment.prevSource()
}
fun nextSource() {
mainFragment.nextSource()
}
fun switchMainFragment() { fun switchMainFragment() {
val transaction = supportFragmentManager.beginTransaction() val transaction = supportFragmentManager.beginTransaction()
@@ -57,7 +64,7 @@ class MainActivity : FragmentActivity() {
transaction.commit() transaction.commit()
} }
fun focusMainFragment() { private fun focusMainFragment() {
mainFragment.focus() mainFragment.focus()
} }
@@ -65,7 +72,7 @@ class MainActivity : FragmentActivity() {
return mainFragment.isHidden return mainFragment.isHidden
} }
fun hideMainFragment() { private fun hideMainFragment() {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.hide(mainFragment) .hide(mainFragment)
@@ -96,31 +103,32 @@ class MainActivity : FragmentActivity() {
} }
KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_MENU -> {
val packageInfo = packageManager.getPackageInfo(packageName, 0)
val versionName = packageInfo.versionName
val textView = TextView(this)
textView.text =
"当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
val imageView = ImageView(this) val imageView = ImageView(this)
val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate) val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate)
imageView.setImageDrawable(drawable) imageView.setImageDrawable(drawable)
val parent = imageView.parent as? ViewGroup
parent?.removeView(imageView)
val linearLayout = LinearLayout(this) val linearLayout = LinearLayout(this)
linearLayout.orientation = LinearLayout.VERTICAL
linearLayout.addView(textView)
linearLayout.addView(imageView) linearLayout.addView(imageView)
val layoutParams = LinearLayout.LayoutParams( val layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT LinearLayout.LayoutParams.WRAP_CONTENT
) )
layoutParams.gravity = Gravity.BOTTOM
imageView.layoutParams = layoutParams imageView.layoutParams = layoutParams
textView.layoutParams = layoutParams
val packageInfo = packageManager.getPackageInfo(packageName, 0)
val versionName = packageInfo.versionName
val builder: AlertDialog.Builder = AlertDialog.Builder(this) val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder builder
.setTitle("当前版本: $versionName, 获取最新: https://github.com/lizongying/my-tv/releases/")
.setView(linearLayout) .setView(linearLayout)
val dialog: AlertDialog = builder.create() val dialog: AlertDialog = builder.create()
@@ -146,13 +154,13 @@ class MainActivity : FragmentActivity() {
KeyEvent.KEYCODE_DPAD_LEFT -> { KeyEvent.KEYCODE_DPAD_LEFT -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prev() prevSource()
} }
} }
KeyEvent.KEYCODE_DPAD_RIGHT -> { KeyEvent.KEYCODE_DPAD_RIGHT -> {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
next() nextSource()
} }
} }
} }
@@ -2,9 +2,7 @@ package com.lizongying.mytv
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.leanback.app.BrowseSupportFragment import androidx.leanback.app.BrowseSupportFragment
import androidx.leanback.widget.ArrayObjectAdapter import androidx.leanback.widget.ArrayObjectAdapter
import androidx.leanback.widget.HeaderItem import androidx.leanback.widget.HeaderItem
@@ -29,24 +27,17 @@ class MainFragment : BrowseSupportFragment() {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
setupUIElements() setupUIElements()
loadRows() loadRows()
setupEventListeners() setupEventListeners()
} }
fun show() {
if (!view?.isVisible!!) {
view?.visibility = View.VISIBLE
}
}
private fun setupUIElements() { private fun setupUIElements() {
// set fastLane (or headers) background color
brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background) brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background)
// headersState = HEADERS_DISABLED // headersState = HEADERS_DISABLED
} }
private var count: Int = 0
private fun loadRows() { private fun loadRows() {
val list = TVList.list val list = TVList.list
val rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
@@ -55,9 +46,21 @@ class MainFragment : BrowseSupportFragment() {
var idx: Long = 0 var idx: Long = 0
for ((k, v) in list) { for ((k, v) in list) {
val listRowAdapter = ArrayObjectAdapter(cardPresenter) val listRowAdapter = ArrayObjectAdapter(cardPresenter)
for ((idx2, v1) in v.withIndex()) { var idx2 = 0
listRowAdapter.add(v1) for ((k1, v1) in v) {
list2.add(Info(idx.toInt(), idx2, v1)) val tv = TV(
count,
k1,
v1.toList()
)
list2.add(
Info(
idx.toInt(), idx2, tv
)
)
listRowAdapter.add(tv)
count++
idx2++
} }
val header = HeaderItem(idx, k) val header = HeaderItem(idx, k)
rowsAdapter.add(ListRow(header, listRowAdapter)) rowsAdapter.add(ListRow(header, listRowAdapter))
@@ -66,7 +69,7 @@ class MainFragment : BrowseSupportFragment() {
adapter = rowsAdapter adapter = rowsAdapter
(activity as? MainActivity)?.play(list.values.first()[0]) (activity as? MainActivity)?.play(list2.first().item as TV)
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
@@ -91,7 +94,7 @@ class MainFragment : BrowseSupportFragment() {
) )
// Toast.makeText( // Toast.makeText(
// activity, // activity,
// "${l.title} $selectedPosition $itemPosition", // "${l.title} ${tv.videoIndex}",
// Toast.LENGTH_SHORT // Toast.LENGTH_SHORT
// ).show() // ).show()
} }
@@ -112,7 +115,45 @@ class MainFragment : BrowseSupportFragment() {
) )
// Toast.makeText( // Toast.makeText(
// activity, // activity,
// "${l.title} $selectedPosition $itemPosition", // "${l.title} ${tv.videoIndex}",
// Toast.LENGTH_SHORT
// ).show()
}
}
fun prevSource() {
view?.post {
val item = list2[itemPosition]
val tv = item.item as TV
tv.videoIndex--
if (tv.videoIndex == -1) {
tv.videoIndex = tv.videoUrl.size - 1
}
(activity as? MainActivity)?.play(tv)
// Toast.makeText(
// activity,
// "${l.title} ${tv.videoIndex}",
// Toast.LENGTH_SHORT
// ).show()
}
}
fun nextSource() {
view?.post {
val item = list2[itemPosition]
val tv = item.item as TV
tv.videoIndex++
if (tv.videoIndex == tv.videoUrl.size) {
tv.videoIndex = 0
}
(activity as? MainActivity)?.play(tv)
// Toast.makeText(
// activity,
// "${l.title} ${tv.videoIndex}",
// Toast.LENGTH_SHORT // Toast.LENGTH_SHORT
// ).show() // ).show()
} }
@@ -33,13 +33,13 @@ class PlaybackControlGlue(
KeyEvent.KEYCODE_DPAD_LEFT -> { KeyEvent.KEYCODE_DPAD_LEFT -> {
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) { if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
(context as? MainActivity)?.prev() (context as? MainActivity)?.prevSource()
} }
} }
KeyEvent.KEYCODE_DPAD_RIGHT -> { KeyEvent.KEYCODE_DPAD_RIGHT -> {
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) { if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
(context as? MainActivity)?.next() (context as? MainActivity)?.nextSource()
} }
} }
} }
@@ -30,17 +30,19 @@ class PlaybackFragment : VideoSupportFragment() {
} }
fun play(tv: TV) { fun play(tv: TV) {
if (tv.videoUrl.isNullOrBlank()) { val videoUrl = tv.videoUrl[tv.videoIndex]
if (videoUrl == "") {
Log.e(TAG, "videoUrl is empty") Log.e(TAG, "videoUrl is empty")
return return
} }
if (tv.videoUrl.equals(lastVideoUrl)) { if (videoUrl == lastVideoUrl) {
Log.e(TAG, "videoUrl is duplication") Log.e(TAG, "videoUrl is duplication")
return return
} }
lastVideoUrl = tv.videoUrl!! lastVideoUrl = videoUrl
playerAdapter?.reset() playerAdapter?.reset()
@@ -50,7 +52,7 @@ class PlaybackFragment : VideoSupportFragment() {
mTransportControlGlue.playWhenPrepared() mTransportControlGlue.playWhenPrepared()
try { try {
playerAdapter?.setDataSource(Uri.parse(tv.videoUrl)) playerAdapter?.setDataSource(Uri.parse(videoUrl))
} catch (e: IOException) { } catch (e: IOException) {
return return
} }
+4 -2
View File
@@ -4,8 +4,9 @@ import java.io.Serializable
data class TV( data class TV(
var id: Int = 0, var id: Int = 0,
var title: String? = null, var title: String,
var videoUrl: String? = null, var videoUrl: List<String>,
var videoIndex: Int = 0,
) : Serializable { ) : Serializable {
override fun toString(): String { override fun toString(): String {
@@ -13,6 +14,7 @@ data class TV(
"id=" + id + "id=" + id +
", title='" + title + '\'' + ", title='" + title + '\'' +
", videoUrl='" + videoUrl + '\'' + ", videoUrl='" + videoUrl + '\'' +
", videoIndex='" + videoIndex + '\'' +
'}' '}'
} }
+43 -59
View File
@@ -1,57 +1,45 @@
package com.lizongying.mytv package com.lizongying.mytv
object TVList { object TVList {
val list: Map<String, List<TV>> by lazy { val list: Map<String, Map<String, Set<String>>> by lazy {
setupTV() setupTV()
} }
private var count: Int = 0
private fun setupTV(): Map<String, List<TV>> { private fun setupTV(): Map<String, Map<String, Set<String>>> {
val tvs = """ val tvs = """
央视频道,CCTV1,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt= 央视频道,CCTV1 综合,http://tvpull.dxhmt.cn/tv/11481-4.m3u8
央视频道,CCTV2,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt= 央视频道,CCTV1 综合,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=
央视频道,CCTV3,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt= 央视频道,CCTV2 财经,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=
央视频道,CCTV4,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt= 央视频道,CCTV3 综艺,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=
央视频道,CCTV4美洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=
央视频道,CCTV4欧洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=
央视频道,CCTV5,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=
央视频道,CCTV5+,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=
央视频道,CCTV6,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv6hd/3000/index.m3u8?&encrypt=
央视频道,CCTV7,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv7hd/51/index.m3u8?&encrypt=
央视频道,CCTV8,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv8hd/3000/index.m3u8?&encrypt=
央视频道,CCTV9,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv9hd/57/index.m3u8?&encrypt=
央视频道,CCTV10,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/cctv10hd/2000/index.m3u8?&encrypt=
央视频道,CCTV11,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv11hd/57/20200103/index.m3u8?&encrypt=
央视频道,CCTV12,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv12hd/57/index.m3u8?&encrypt=
央视频道,CCTV13,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctv13/2000/index.m3u8?&encrypt=
央视频道,CCTV14,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv14hd/3000/index.m3u8?&encrypt=
央视频道,CCTV15,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv15hd/51/index.m3u8?&encrypt=
央视频道,CCTV17,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv17hd/57/index.m3u8?&encrypt=
央视频道,CGTN,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctvnews/1000/index.m3u8?&encrypt=
央视频道,CCTV2,https://iptv.luas.edu.cn/liverespath/6b13fe5368d391761312a985ace065c0ecad2f5e/877097d2fa-0-0-b7736e6941fd5cb71f45ef9397b68092/index.m3u8
央视频道,CCTV3,https://iptv.luas.edu.cn/liverespath/f76f9947c68be18d7a456e25aa59a08c5747e6a5/0df24da9ec-0-0-dca40ddadd2a051ce1a83536d9310820/index.m3u8
央视频道,CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8 央视频道,CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8
央视频道,CCTV6,https://iptv.luas.edu.cn/liverespath/f2f39ee2105c85c32df375728a51b5d89d3afab4/113ed89f48-0-0-85216e55861329ec31ba1437a2ff37c9/index.m3u8 央视频道,CCTV4 中文国际,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=
央视频道,CCTV7,https://iptv.luas.edu.cn/liverespath/f116a0a5035935a3435155998163d8eaa60554c3/4ba629f762-0-0-0a66b18805ff859ce68ab1137157079e/index.m3u8 央视频道,CCTV4 中文国际美洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=
央视频道,CCTV8,https://iptv.luas.edu.cn/liverespath/9e6e3b618b5dc902d992949f0c669bb674f6cde8/cae4471c68-0-0-316245c664c3311072c7279ec29672fe/index.m3u8 央视频道,CCTV4 中文国际欧洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=
央视频道,CCTV9,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8 央视频道,CCTV5 体育,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=
央视频道,CCTV10,https://iptv.luas.edu.cn/liverespath/924e6292a0e6f7440e8380075908fed9915b3c00/780499a193-0-0-c50d57fd93cf32a5144ec8d889eb3ed9/index.m3u8 央视频道,CCTV5+ 体育赛事,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=
央视频道,CCTV11,https://iptv.luas.edu.cn/liverespath/3d0aa0f1604f13f0ae90c2dc0590ca22a1bcaaa2/646e868153-0-0-a53121a1df5b93a74d1980270e652878/index.m3u8 央视频道,CCTV6 电影,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv6hd/3000/index.m3u8?&encrypt=
央视频道,CCTV12,https://iptv.luas.edu.cn/liverespath/fb84bde1de15cdb0308a7910cebc0497594ae94e/e2d8885f42-0-0-6e1f55986652ba786606a116c5cc0775/index.m3u8 央视频道,CCTV7 国防军事,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv7hd/51/index.m3u8?&encrypt=
央视频道,CCTV13,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8 央视频道,CCTV8 电视剧,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv8hd/3000/index.m3u8?&encrypt=
央视频道,CCTV14,https://iptv.luas.edu.cn/liverespath/0e0973e58d4835f4b872548164462930003f77b4/0ef01acfdb-0-0-3c10e95bcde07ba14f0f93a8d831b684/index.m3u8 央视频道,CCTV9 记录,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv9hd/57/index.m3u8?&encrypt=
央视频道,CCTV10 科教,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/cctv10hd/2000/index.m3u8?&encrypt=
央视频道,CCTV11 戏曲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv11hd/57/20200103/index.m3u8?&encrypt=
央视频道,CCTV12 社会与法,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv12hd/57/index.m3u8?&encrypt=
央视频道,CCTV13 新闻,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8
央视频道,CCTV13 新闻,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctv13/2000/index.m3u8?&encrypt=
央视频道,CCTV14 少儿,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv14hd/3000/index.m3u8?&encrypt=
央视频道,CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8 央视频道,CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8
央视频道,CCTV15 音乐,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv15hd/51/index.m3u8?&encrypt=
央视频道,CCTV17 农业农村,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv17hd/57/index.m3u8?&encrypt=
央视频道,CGTN,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctvnews/1000/index.m3u8?&encrypt=
央视频道,CGTN 新闻频道,http://live.cgtn.com/1000/prog_index.m3u8
央视频道,CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
央视频道,CGTN 法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8
央视频道,CGTN 俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8
央视频道,CGTN 西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8
央视频道,CGTN 阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8
央视频道,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
CGTN,新闻频道,http://live.cgtn.com/1000/prog_index.m3u8
CGTN,纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
CGTN,法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8
CGTN,俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8
CGTN,西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8
CGTN,阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8
CGTN,拉丁美洲频道,http://livees.cgtn.com/1000e/prog_index.m3u8
地方频道,东方卫视,http://hlsbkmgsplive.miguvideo.com/wd_r4/dfl/dongfangwshd/3000/index.m3u8?&encrypt= 地方频道,东方卫视,http://hlsbkmgsplive.miguvideo.com/wd_r4/dfl/dongfangwshd/3000/index.m3u8?&encrypt=
地方频道,内蒙古卫视,http://hlsbkmgsplive.miguvideo.com/envivo_w/2018/SD/neimeng/1000/index.m3u8?&encrypt= 地方频道,内蒙古卫视,http://hlsbkmgsplive.miguvideo.com/envivo_w/2018/SD/neimeng/1000/index.m3u8?&encrypt=
地方频道,湖南卫视,http://hlsbkmgsplive.miguvideo.com/wd-hunanhd-2500/index.m3u8?&encrypt= 地方频道,湖南卫视,http://hlsbkmgsplive.miguvideo.com/wd-hunanhd-2500/index.m3u8?&encrypt=
@@ -107,26 +95,22 @@ CGTN,拉丁美洲频道,http://livees.cgtn.com/1000e/prog_index.m3u8
地方频道,延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8 地方频道,延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8
""".trimIndent() """.trimIndent()
val map: MutableMap<String, MutableList<TV>> = mutableMapOf() val map: MutableMap<String, MutableMap<String, LinkedHashSet<String>>> = mutableMapOf()
for (i in tvs.split("\n")) { for (i in tvs.split("\n")) {
val (channel, title, videoUrl) = i.split(",") if (!i.contains(",")) {
val videoList = map[channel] ?: mutableListOf() continue
videoList.add(buildTV(title, videoUrl)) }
map[channel] = videoList val (channel, title, url) = i.split(",")
val titleMap = map[channel] ?: mutableMapOf()
val urlSet = titleMap[title] ?: LinkedHashSet()
urlSet.add(url)
titleMap[title] = urlSet
map[channel] = titleMap
} }
return map return map
} }
private fun buildTV(
title: String,
videoUrl: String,
): TV {
val tv = TV()
tv.id = count++
tv.title = title
tv.videoUrl = videoUrl
return tv
}
} }