diff --git a/app/build.gradle b/app/build.gradle index d8ea8c3..2ebea44 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,7 +46,7 @@ static def VersionName() { try { def process = 'git describe --tags --always'.execute() process.waitFor() - return process.text.trim() + return process.text.trim() - "v" } catch (ignored) { return "1.0.0" } diff --git a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt index d4fce2f..2368c79 100644 --- a/app/src/main/java/com/lizongying/mytv/CardPresenter.kt +++ b/app/src/main/java/com/lizongying/mytv/CardPresenter.kt @@ -32,10 +32,9 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese val tv = item as TV val cardView = viewHolder.view as ImageCardView - if (tv.videoUrl != null) { - cardView.titleText = tv.title - cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) - cardView.tag = tv.videoUrl + 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) } @@ -44,7 +43,6 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese // cardView.mainImageView.setImageBitmap(videoThumbnail) // } // } - } } override fun onUnbindViewHolder(viewHolder: ViewHolder) { diff --git a/app/src/main/java/com/lizongying/mytv/MainActivity.kt b/app/src/main/java/com/lizongying/mytv/MainActivity.kt index 346a7de..b87182f 100644 --- a/app/src/main/java/com/lizongying/mytv/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv/MainActivity.kt @@ -44,6 +44,14 @@ class MainActivity : FragmentActivity() { mainFragment.next() } + fun prevSource() { + mainFragment.prevSource() + } + + fun nextSource() { + mainFragment.nextSource() + } + fun switchMainFragment() { val transaction = supportFragmentManager.beginTransaction() @@ -57,7 +65,7 @@ class MainActivity : FragmentActivity() { transaction.commit() } - fun focusMainFragment() { + private fun focusMainFragment() { mainFragment.focus() } @@ -65,7 +73,7 @@ class MainActivity : FragmentActivity() { return mainFragment.isHidden } - fun hideMainFragment() { + private fun hideMainFragment() { if (!mainFragment.isHidden) { supportFragmentManager.beginTransaction() .hide(mainFragment) @@ -146,13 +154,13 @@ class MainActivity : FragmentActivity() { KeyEvent.KEYCODE_DPAD_LEFT -> { if (mainFragment.isHidden) { - prev() + prevSource() } } KeyEvent.KEYCODE_DPAD_RIGHT -> { if (mainFragment.isHidden) { - next() + nextSource() } } } diff --git a/app/src/main/java/com/lizongying/mytv/MainFragment.kt b/app/src/main/java/com/lizongying/mytv/MainFragment.kt index 0eb24f5..2a993cd 100644 --- a/app/src/main/java/com/lizongying/mytv/MainFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/MainFragment.kt @@ -2,9 +2,7 @@ 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 @@ -29,24 +27,17 @@ class MainFragment : BrowseSupportFragment() { 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 var count: Int = 0 + private fun loadRows() { val list = TVList.list val rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) @@ -55,9 +46,20 @@ class MainFragment : BrowseSupportFragment() { var idx: Long = 0 for ((k, v) in list) { val listRowAdapter = ArrayObjectAdapter(cardPresenter) - for ((idx2, v1) in v.withIndex()) { + var idx2 = 0 + for ((k1, v1) in v) { listRowAdapter.add(v1) - list2.add(Info(idx.toInt(), idx2, v1)) + list2.add( + Info( + idx.toInt(), idx2, TV( + count, + k1, + v1.toList() + ) + ) + ) + count++ + idx2++ } val header = HeaderItem(idx, k) rowsAdapter.add(ListRow(header, listRowAdapter)) @@ -66,7 +68,7 @@ class MainFragment : BrowseSupportFragment() { adapter = rowsAdapter - (activity as? MainActivity)?.play(list.values.first()[0]) + (activity as? MainActivity)?.play(list2.first().item as TV) (activity as? MainActivity)?.switchMainFragment() } @@ -91,7 +93,7 @@ class MainFragment : BrowseSupportFragment() { ) // Toast.makeText( // activity, -// "${l.title} $selectedPosition $itemPosition", +// "${l.title} ${tv.videoIndex}", // Toast.LENGTH_SHORT // ).show() } @@ -112,7 +114,45 @@ class MainFragment : BrowseSupportFragment() { ) // Toast.makeText( // 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 // ).show() } diff --git a/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt b/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt index 057a607..751cc49 100644 --- a/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt +++ b/app/src/main/java/com/lizongying/mytv/PlaybackControlGlue.kt @@ -33,13 +33,13 @@ class PlaybackControlGlue( KeyEvent.KEYCODE_DPAD_LEFT -> { if ((context as? MainActivity)?.mainFragmentIsHidden() == true) { - (context as? MainActivity)?.prev() + (context as? MainActivity)?.prevSource() } } KeyEvent.KEYCODE_DPAD_RIGHT -> { if ((context as? MainActivity)?.mainFragmentIsHidden() == true) { - (context as? MainActivity)?.next() + (context as? MainActivity)?.nextSource() } } } diff --git a/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt b/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt index 2148d06..7edb3fa 100644 --- a/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/PlaybackFragment.kt @@ -30,17 +30,14 @@ class PlaybackFragment : VideoSupportFragment() { } fun play(tv: TV) { - if (tv.videoUrl.isNullOrBlank()) { - Log.e(TAG, "videoUrl is empty") - return - } + val videoUrl = tv.videoUrl[tv.videoIndex] - if (tv.videoUrl.equals(lastVideoUrl)) { + if (videoUrl == lastVideoUrl) { Log.e(TAG, "videoUrl is duplication") return } - lastVideoUrl = tv.videoUrl!! + lastVideoUrl = videoUrl playerAdapter?.reset() @@ -50,7 +47,7 @@ class PlaybackFragment : VideoSupportFragment() { mTransportControlGlue.playWhenPrepared() try { - playerAdapter?.setDataSource(Uri.parse(tv.videoUrl)) + playerAdapter?.setDataSource(Uri.parse(videoUrl)) } catch (e: IOException) { return } diff --git a/app/src/main/java/com/lizongying/mytv/TV.kt b/app/src/main/java/com/lizongying/mytv/TV.kt index dd5da07..bd4df80 100644 --- a/app/src/main/java/com/lizongying/mytv/TV.kt +++ b/app/src/main/java/com/lizongying/mytv/TV.kt @@ -4,8 +4,9 @@ import java.io.Serializable data class TV( var id: Int = 0, - var title: String? = null, - var videoUrl: String? = null, + var title: String, + var videoUrl: List, + var videoIndex: Int = 0, ) : Serializable { override fun toString(): String { @@ -13,6 +14,7 @@ data class TV( "id=" + id + ", title='" + title + '\'' + ", videoUrl='" + videoUrl + '\'' + + ", videoIndex='" + videoIndex + '\'' + '}' } diff --git a/app/src/main/java/com/lizongying/mytv/TVList.kt b/app/src/main/java/com/lizongying/mytv/TVList.kt index 2cc5076..aa595a8 100644 --- a/app/src/main/java/com/lizongying/mytv/TVList.kt +++ b/app/src/main/java/com/lizongying/mytv/TVList.kt @@ -1,57 +1,54 @@ package com.lizongying.mytv object TVList { - val list: Map> by lazy { + val list: Map>> by lazy { setupTV() } - private var count: Int = 0 - private fun setupTV(): Map> { + private fun setupTV(): Map>> { val tvs = """ 央视频道,CCTV1,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt= +央视频道,CCTV2,https://iptv.luas.edu.cn/liverespath/6b13fe5368d391761312a985ace065c0ecad2f5e/877097d2fa-0-0-b7736e6941fd5cb71f45ef9397b68092/index.m3u8 央视频道,CCTV2,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt= +央视频道,CCTV3,https://iptv.luas.edu.cn/liverespath/f76f9947c68be18d7a456e25aa59a08c5747e6a5/0df24da9ec-0-0-dca40ddadd2a051ce1a83536d9310820/index.m3u8 央视频道,CCTV3,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt= -央视频道,CCTV4,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/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= +央视频道,CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8 +央视频道,CCTV4 中文国际,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/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,https://iptv.luas.edu.cn/liverespath/f2f39ee2105c85c32df375728a51b5d89d3afab4/113ed89f48-0-0-85216e55861329ec31ba1437a2ff37c9/index.m3u8 央视频道,CCTV6,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv6hd/3000/index.m3u8?&encrypt= +央视频道,CCTV7,https://iptv.luas.edu.cn/liverespath/f116a0a5035935a3435155998163d8eaa60554c3/4ba629f762-0-0-0a66b18805ff859ce68ab1137157079e/index.m3u8 央视频道,CCTV7,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv7hd/51/index.m3u8?&encrypt= +央视频道,CCTV8,https://iptv.luas.edu.cn/liverespath/9e6e3b618b5dc902d992949f0c669bb674f6cde8/cae4471c68-0-0-316245c664c3311072c7279ec29672fe/index.m3u8 央视频道,CCTV8,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv8hd/3000/index.m3u8?&encrypt= +央视频道,CCTV9,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8 央视频道,CCTV9,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv9hd/57/index.m3u8?&encrypt= +央视频道,CCTV10,https://iptv.luas.edu.cn/liverespath/924e6292a0e6f7440e8380075908fed9915b3c00/780499a193-0-0-c50d57fd93cf32a5144ec8d889eb3ed9/index.m3u8 央视频道,CCTV10,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/cctv10hd/2000/index.m3u8?&encrypt= +央视频道,CCTV11,https://iptv.luas.edu.cn/liverespath/3d0aa0f1604f13f0ae90c2dc0590ca22a1bcaaa2/646e868153-0-0-a53121a1df5b93a74d1980270e652878/index.m3u8 央视频道,CCTV11,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv11hd/57/20200103/index.m3u8?&encrypt= +央视频道,CCTV12,https://iptv.luas.edu.cn/liverespath/fb84bde1de15cdb0308a7910cebc0497594ae94e/e2d8885f42-0-0-6e1f55986652ba786606a116c5cc0775/index.m3u8 央视频道,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,https://iptv.luas.edu.cn/liverespath/0e0973e58d4835f4b872548164462930003f77b4/0ef01acfdb-0-0-3c10e95bcde07ba14f0f93a8d831b684/index.m3u8 央视频道,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://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 -央视频道,CCTV6,https://iptv.luas.edu.cn/liverespath/f2f39ee2105c85c32df375728a51b5d89d3afab4/113ed89f48-0-0-85216e55861329ec31ba1437a2ff37c9/index.m3u8 -央视频道,CCTV7,https://iptv.luas.edu.cn/liverespath/f116a0a5035935a3435155998163d8eaa60554c3/4ba629f762-0-0-0a66b18805ff859ce68ab1137157079e/index.m3u8 -央视频道,CCTV8,https://iptv.luas.edu.cn/liverespath/9e6e3b618b5dc902d992949f0c669bb674f6cde8/cae4471c68-0-0-316245c664c3311072c7279ec29672fe/index.m3u8 -央视频道,CCTV9,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8 -央视频道,CCTV10,https://iptv.luas.edu.cn/liverespath/924e6292a0e6f7440e8380075908fed9915b3c00/780499a193-0-0-c50d57fd93cf32a5144ec8d889eb3ed9/index.m3u8 -央视频道,CCTV11,https://iptv.luas.edu.cn/liverespath/3d0aa0f1604f13f0ae90c2dc0590ca22a1bcaaa2/646e868153-0-0-a53121a1df5b93a74d1980270e652878/index.m3u8 -央视频道,CCTV12,https://iptv.luas.edu.cn/liverespath/fb84bde1de15cdb0308a7910cebc0497594ae94e/e2d8885f42-0-0-6e1f55986652ba786606a116c5cc0775/index.m3u8 -央视频道,CCTV13,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8 -央视频道,CCTV14,https://iptv.luas.edu.cn/liverespath/0e0973e58d4835f4b872548164462930003f77b4/0ef01acfdb-0-0-3c10e95bcde07ba14f0f93a8d831b684/index.m3u8 -央视频道,CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.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://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/envivo_w/2018/SD/neimeng/1000/index.m3u8?&encrypt= 地方频道,湖南卫视,http://hlsbkmgsplive.miguvideo.com/wd-hunanhd-2500/index.m3u8?&encrypt= @@ -107,26 +104,19 @@ CGTN,拉丁美洲频道,http://livees.cgtn.com/1000e/prog_index.m3u8 地方频道,延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8 """.trimIndent() - val map: MutableMap> = mutableMapOf() + val map: MutableMap>> = mutableMapOf() for (i in tvs.split("\n")) { - val (channel, title, videoUrl) = i.split(",") - val videoList = map[channel] ?: mutableListOf() - 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 } - - private fun buildTV( - title: String, - videoUrl: String, - ): TV { - val tv = TV() - tv.id = count++ - tv.title = title - tv.videoUrl = videoUrl - return tv - } } \ No newline at end of file