Compare commits

..
3 Commits
Author SHA1 Message Date
Li ZongYing e6530b4e9e fix still playing after exit 2023-12-27 18:17:13 +08:00
Li ZongYing c759ada907 fix still playing after exit 2023-12-27 16:26:05 +08:00
Li ZongYing bb4fd56845 fix video size on phone 2023-12-26 11:21:03 +08:00
17 changed files with 262 additions and 203 deletions
+11 -2
View File
@@ -4,13 +4,22 @@
## 使用 ## 使用
下载安装[releases](https://github.com/lizongying/my-tv/releases) 下载安装 [releases](https://github.com/lizongying/my-tv/releases/)
[my-tv](https://lyrics.run/my-tv.html) 其他地址 [my-tv](https://lyrics.run/my-tv.html)
![image](./screenshots/img.png) ![image](./screenshots/img.png)
![image](./screenshots/img_1.png) ![image](./screenshots/img_1.png)
## 更新日志
### v1.2.4
* 改变换台滑动方向,上一个频道下滑,下一个频道上滑
* 软件退出时,退出播放器
* 播放相同的频道,不再重复加载
* 暂时移除部分频道
## 其他 ## 其他
小米电视可以使用小米电视助手进行安装 小米电视可以使用小米电视助手进行安装
+3 -3
View File
@@ -16,10 +16,10 @@
<activity <activity
android:keepScreenOn="true" android:keepScreenOn="true"
android:name=".MainActivity" android:name=".MainActivity"
android:banner="@drawable/tv" android:banner="@drawable/logo"
android:exported="true" android:exported="true"
android:icon="@drawable/tv" android:icon="@drawable/logo"
android:logo="@drawable/tv" android:logo="@drawable/logo"
android:screenOrientation="landscape"> android:screenOrientation="landscape">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -1,23 +0,0 @@
package com.lizongying.mytv
import androidx.media3.common.C
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy
@UnstableApi
class CustomLoadErrorHandlingPolicy(private val minimumLoadableRetryCount: Int) :
DefaultLoadErrorHandlingPolicy(minimumLoadableRetryCount) {
override fun getMinimumLoadableRetryCount(dataType: Int): Int {
return if (minimumLoadableRetryCount == -1) {
if (dataType == C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE) {
DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE
} else {
DEFAULT_MIN_LOADABLE_RETRY_COUNT
}
} else {
minimumLoadableRetryCount
}
}
}
@@ -13,6 +13,7 @@ import android.util.Log
import android.view.GestureDetector import android.view.GestureDetector
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
import android.view.WindowManager import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
@@ -26,7 +27,7 @@ import java.security.MessageDigest
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
private val playbackFragment = PlaybackFragment() var playerFragment = PlayerFragment()
private val mainFragment = MainFragment() private val mainFragment = MainFragment()
private val infoFragment = InfoFragment() private val infoFragment = InfoFragment()
@@ -40,14 +41,16 @@ class MainActivity : FragmentActivity() {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_HIDE_NAVIGATION
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, playbackFragment) .add(R.id.main_browse_fragment, playerFragment)
.add(R.id.main_browse_fragment, mainFragment) .add(R.id.main_browse_fragment, mainFragment)
.add(R.id.main_browse_fragment, infoFragment) .add(R.id.main_browse_fragment, infoFragment)
.hide(infoFragment) .hide(infoFragment)
.commit() .commit()
mainFragment.view?.requestFocus()
} }
gestureDetector = GestureDetector(this, GestureListener()) gestureDetector = GestureDetector(this, GestureListener())
} }
@@ -75,7 +78,8 @@ class MainActivity : FragmentActivity() {
fun play(tvViewModel: TVViewModel) { fun play(tvViewModel: TVViewModel) {
Log.i(TAG, "play: ${tvViewModel.getTV()}") Log.i(TAG, "play: ${tvViewModel.getTV()}")
playbackFragment.play(tvViewModel) playerFragment.play(tvViewModel)
mainFragment.view?.requestFocus()
} }
fun prev() { fun prev() {
@@ -86,11 +90,11 @@ class MainActivity : FragmentActivity() {
mainFragment.next() mainFragment.next()
} }
fun prevSource() { private fun prevSource() {
mainFragment.prevSource() mainFragment.prevSource()
} }
fun nextSource() { private fun nextSource() {
mainFragment.nextSource() mainFragment.nextSource()
} }
@@ -111,7 +115,7 @@ class MainActivity : FragmentActivity() {
mainFragment.focus() mainFragment.focus()
} }
fun mainFragmentIsHidden(): Boolean { private fun mainFragmentIsHidden(): Boolean {
return mainFragment.isHidden return mainFragment.isHidden
} }
@@ -124,7 +128,6 @@ class MainActivity : FragmentActivity() {
} }
override fun onTouchEvent(event: MotionEvent?): Boolean { override fun onTouchEvent(event: MotionEvent?): Boolean {
// 在触摸事件中将事件传递给 GestureDetector 处理手势
if (event != null) { if (event != null) {
gestureDetector.onTouchEvent(event) gestureDetector.onTouchEvent(event)
} }
@@ -134,6 +137,7 @@ class MainActivity : FragmentActivity() {
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() { private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean { override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
Log.i(TAG, "onSingleTapConfirmed")
switchMainFragment() switchMainFragment()
return true return true
} }
@@ -144,30 +148,27 @@ class MainActivity : FragmentActivity() {
velocityX: Float, velocityX: Float,
velocityY: Float velocityY: Float
): Boolean { ): Boolean {
// 如果 Y 方向的速度为负值,表示向上滑动 if (velocityY > 0) {
if (velocityY < 0) {
// 在这里执行上滑时的操作
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prev() prev()
} else { } else {
if (mainFragment.selectedPosition == 0) { // if (mainFragment.selectedPosition == 0) {
mainFragment.setSelectedPosition( // mainFragment.setSelectedPosition(
mainFragment.tvListViewModel.maxNum.size - 1, // mainFragment.tvListViewModel.maxNum.size - 1,
false // false
) // )
} // }
} }
} }
if (velocityY > 0) { if (velocityY < 0) {
// 在这里执行上滑时的操作
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
next() next()
} else { } else {
if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) { // if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) {
// mainFragment.setSelectedPosition(0, false) //// mainFragment.setSelectedPosition(0, false)
hideMainFragment() // hideMainFragment()
return false // return false
} // }
} }
} }
return super.onFling(e1, e2, velocityX, velocityY) return super.onFling(e1, e2, velocityX, velocityY)
@@ -240,12 +241,12 @@ class MainActivity : FragmentActivity() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prev() prev()
} else { } else {
if (mainFragment.selectedPosition == 0) { // if (mainFragment.selectedPosition == 0) {
mainFragment.setSelectedPosition( // mainFragment.setSelectedPosition(
mainFragment.tvListViewModel.maxNum.size - 1, // mainFragment.tvListViewModel.maxNum.size - 1,
false // false
) // )
} // }
} }
} }
@@ -253,11 +254,11 @@ class MainActivity : FragmentActivity() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
next() next()
} else { } else {
if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) { // if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) {
// mainFragment.setSelectedPosition(0, false) //// mainFragment.setSelectedPosition(0, false)
hideMainFragment() // hideMainFragment()
return false // return false
} // }
} }
} }
@@ -265,12 +266,12 @@ class MainActivity : FragmentActivity() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
prevSource() prevSource()
} else { } else {
if (mainFragment.tvListViewModel.getTVViewModelCurrent() // if (mainFragment.tvListViewModel.getTVViewModelCurrent()
?.getItemPosition() == 0 // ?.getItemPosition() == 0
) { // ) {
// mainFragment.toLastPosition() //// mainFragment.toLastPosition()
hideMainFragment() // hideMainFragment()
} // }
} }
} }
@@ -278,11 +279,11 @@ class MainActivity : FragmentActivity() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
nextSource() nextSource()
} else { } else {
if (mainFragment.tvListViewModel.getTVViewModelCurrent() // if (mainFragment.tvListViewModel.getTVViewModelCurrent()
?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1 // ?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1
) { // ) {
mainFragment.toFirstPosition() // mainFragment.toFirstPosition()
} // }
} }
} }
} }
@@ -33,6 +33,9 @@ class MainFragment : BrowseSupportFragment() {
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private var sharedPref: SharedPreferences? = null private var sharedPref: SharedPreferences? = null
private var lastVideoUrl: String = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
headersState = HEADERS_DISABLED headersState = HEADERS_DISABLED
@@ -53,35 +56,51 @@ class MainFragment : BrowseSupportFragment() {
} }
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel -> tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel ->
tvViewModel.ready.observe(viewLifecycleOwner) { _ -> tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.ready.value != null) {
// not first time && channel not change
if (tvViewModel.ready.value != null
&& tvViewModel.id.value == itemPosition
&& check(tvViewModel)
) {
Log.i(TAG, "ready ${tvViewModel.title.value}") Log.i(TAG, "ready ${tvViewModel.title.value}")
if (tvViewModel.id.value == itemPosition) { (activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.play(tvViewModel) // (activity as? MainActivity)?.switchInfoFragment(item)
// (activity as? MainActivity)?.switchInfoFragment(tv)
}
} }
} }
tvViewModel.change.observe(viewLifecycleOwner) { _ -> tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) { if (tvViewModel.change.value != null) {
Log.i(TAG, "switch to ${tvViewModel.title.value}") val title = tvViewModel.title.value
Log.i(TAG, "switch $title")
if (tvViewModel.ysp() != null) { if (tvViewModel.ysp() != null) {
Log.i(TAG, "${tvViewModel.title.value} to get ysp") Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { request?.fetchData(it) } tvViewModel.let { request?.fetchData(it) }
} }
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
Toast.makeText(
activity,
title,
Toast.LENGTH_SHORT
).show()
} else { } else {
(activity as? MainActivity)?.play(tvViewModel) if (check(tvViewModel)) {
(activity as? MainActivity)?.play(tvViewModel)
// (activity as? MainActivity)?.switchInfoFragment(item) // (activity as? MainActivity)?.switchInfoFragment(item)
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
Toast.makeText(
activity,
title,
Toast.LENGTH_SHORT
).show()
}
} }
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
Toast.makeText(
activity,
tvViewModel.title.value,
Toast.LENGTH_SHORT
).show()
} }
} }
tvViewModel.program.observe(viewLifecycleOwner) { _ -> tvViewModel.program.observe(viewLifecycleOwner) { _ ->
@@ -95,15 +114,20 @@ class MainFragment : BrowseSupportFragment() {
} }
} }
fun checkProgram() { fun check(tvViewModel: TVViewModel): Boolean {
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel -> val title = tvViewModel.title.value
if (tvViewModel.program.value!!.isEmpty()) { val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
if (tvViewModel.programId.value != null) { if (videoUrl == null || videoUrl == "") {
Log.i(TAG, "get program ${tvViewModel.title.value}") Log.e(TAG, "$title videoUrl is empty")
request?.fetchProgram(tvViewModel) return false
}
}
} }
if (videoUrl == lastVideoUrl) {
Log.e(TAG, "$title videoUrl is duplication")
return false
}
return true
} }
fun toLastPosition() { fun toLastPosition() {
@@ -245,12 +269,13 @@ class MainFragment : BrowseSupportFragment() {
row: Row row: Row
) { ) {
if (item is TVViewModel) { if (item is TVViewModel) {
itemPosition = item.id.value!! if (itemPosition != item.id.value!!) {
savePosition(itemPosition) itemPosition = item.id.value!!
savePosition(itemPosition)
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
}
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
} }
@@ -261,23 +286,17 @@ class MainFragment : BrowseSupportFragment() {
itemViewHolder: Presenter.ViewHolder?, item: Any?, itemViewHolder: Presenter.ViewHolder?, item: Any?,
rowViewHolder: RowPresenter.ViewHolder, row: Row rowViewHolder: RowPresenter.ViewHolder, row: Row
) { ) {
if (itemViewHolder == null) {
view?.post {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvViewModel != null) {
setSelectedPosition(
tvViewModel.getRowPosition(), false,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
}
}
}
if (item is TVViewModel) { if (item is TVViewModel) {
tvListViewModel.setItemPositionCurrent(item.id.value!!) tvListViewModel.setItemPositionCurrent(item.id.value!!)
} }
} }
} }
override fun onResume() {
super.onResume()
view!!.requestFocus()
}
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
} }
@@ -1,66 +0,0 @@
package com.lizongying.mytv
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.OptIn
import androidx.fragment.app.Fragment
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import com.lizongying.mytv.databinding.PlayerBinding
import com.lizongying.mytv.models.TVViewModel
class PlaybackFragment : Fragment() {
private var lastVideoUrl: String = ""
private var _binding: PlayerBinding? = null
private var playerView: PlayerView? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = PlayerBinding.inflate(inflater, container, false)
playerView = _binding!!.playerView
return _binding!!.root
}
@OptIn(UnstableApi::class)
fun play(tvModel: TVViewModel) {
val videoUrl = tvModel.videoIndex.value?.let { tvModel.videoUrl.value?.get(it) }
if (videoUrl == null || videoUrl == "") {
Log.e(TAG, "${tvModel.title.value} videoUrl is empty")
return
}
if (videoUrl == lastVideoUrl) {
Log.e(TAG, "videoUrl is duplication")
return
}
lastVideoUrl = videoUrl
if (playerView!!.player == null) {
playerView!!.player = activity?.let {
ExoPlayer.Builder(it)
.build()
}
playerView!!.player?.playWhenReady = true
}
playerView!!.player?.run {
setMediaItem(MediaItem.fromUri(videoUrl))
prepare()
}
}
companion object {
private const val TAG = "PlaybackVideoFragment"
}
}
@@ -0,0 +1,115 @@
package com.lizongying.mytv
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.annotation.OptIn
import androidx.fragment.app.Fragment
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import com.lizongying.mytv.databinding.PlayerBinding
import com.lizongying.mytv.models.TVViewModel
class PlayerFragment : Fragment() {
private var _binding: PlayerBinding? = null
private var playerView: PlayerView? = null
private var videoUrl: String? = null
private var tvViewModel: TVViewModel? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = PlayerBinding.inflate(inflater, container, false)
playerView = _binding!!.playerView
(activity as MainActivity).playerFragment = this
playerView?.viewTreeObserver?.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
playerView!!.viewTreeObserver.removeOnGlobalLayoutListener(this)
playerView!!.player = activity?.let {
ExoPlayer.Builder(it)
.build()
}
playerView!!.player?.playWhenReady = true
playerView!!.player?.addListener(object : Player.Listener {
override fun onVideoSizeChanged(videoSize: VideoSize) {
val aspectRatio = 16f / 9f
val layoutParams = playerView?.layoutParams
layoutParams?.width =
(playerView?.measuredHeight?.times(aspectRatio))?.toInt()
playerView?.layoutParams = layoutParams
}
//
// override fun onPlayerError(error: PlaybackException) {
// super.onPlayerError(error)
// }
})
if (videoUrl !== null) {
playerView!!.player?.run {
videoUrl?.let { MediaItem.fromUri(it) }?.let { setMediaItem(it) }
prepare()
}
videoUrl = null
}
}
})
Log.i(TAG, "PlayerFragment onCreateView")
return _binding!!.root
}
@OptIn(UnstableApi::class)
fun play(tvViewModel: TVViewModel) {
this.tvViewModel = tvViewModel
val videoUrlCurrent =
tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
if (playerView == null || playerView?.player == null) {
Log.i(TAG, "playerView not ready $view}")
videoUrl = videoUrlCurrent
} else {
Log.i(TAG, "playerView ok")
playerView?.player?.run {
val mediaItem = MediaItem.Builder()
tvViewModel.id.value?.let { mediaItem.setMediaId(it.toString()) }
videoUrlCurrent?.let { mediaItem.setUri(it) }
setMediaItem(mediaItem.build())
prepare()
}
}
}
override fun onPause() {
super.onPause()
if (playerView != null) {
playerView!!.player?.stop()
}
}
override fun onResume() {
super.onResume()
if (playerView != null) {
playerView!!.player?.play()
}
}
override fun onDestroy() {
super.onDestroy()
if (playerView != null) {
playerView!!.player?.release()
}
}
companion object {
private const val TAG = "PlaybackVideoFragment"
}
}
@@ -29,6 +29,12 @@ CCTV14 少儿,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/inde
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 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
CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8;http://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;http://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://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 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://live.cgtn.com/1000/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp,600014550,2001656803
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://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/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 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp,600084781,2010155403
电视指南,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
@@ -40,12 +46,6 @@ CCTV17 农业农村,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/322122619
兵器科技,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8 兵器科技,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8
高尔夫网球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8 高尔夫网球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8
央视文化精品,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8 央视文化精品,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8
CGTN,http://live.cgtn.com/1000/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp,600014550,2001656803
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://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/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 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp,600084781,2010155403
地方频道 地方频道
东方卫视,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://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
@@ -69,6 +69,11 @@ CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8,https://resource
四川卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp,600002516,2000295003 四川卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp,600002516,2000295003
东南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp,600002484,2000292503 东南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp,600002484,2000292503
海南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp,600002506,2000291503 海南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8,https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp,600002506,2000291503
三沙卫视,https://pullsstv90080111.ssws.tv/live/SSTV20220729.m3u8
移动专区
天津卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225740/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226204/index.m3u8 天津卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225740/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226204/index.m3u8
吉林卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226397/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225792/index.m3u8 吉林卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226397/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225792/index.m3u8
云南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226444/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225751/index.m3u8 云南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226444/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225751/index.m3u8
@@ -84,9 +89,9 @@ CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8,https://resource
三沙卫视,https://pullsstv90080111.ssws.tv/live/SSTV20220729.m3u8 三沙卫视,https://pullsstv90080111.ssws.tv/live/SSTV20220729.m3u8
延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226220/index.m3u8 延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226220/index.m3u8
浙江少儿,http://hw-m-l.cztv.com/channels/lantian/channel008/1080p.m3u8 浙江少儿,http://hw-m-l.cztv.com/channels/lantian/channel008/1080p.m3u8
南京少儿,http://live.nbs.cn/channels/njtv/sepd/500k.m3u8
移动专区
CETV1,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225753/index.m3u8 CETV1,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225753/index.m3u8
CETV2,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225756/index.m3u8 CETV2,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225756/index.m3u8
CETV3,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226226/index.m3u8 CETV3,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226226/index.m3u8
@@ -161,6 +166,9 @@ CHC动作电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226465/in
} }
if (!i.contains(",")) { if (!i.contains(",")) {
channel = i.trim() channel = i.trim()
if (channel == "移动专区") {
break
}
continue continue
} }
val p = i.split(",") val p = i.split(",")
@@ -2,11 +2,13 @@ package com.lizongying.mytv.api
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log
import com.lizongying.mytv.Encryptor import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import java.util.Date import java.util.Date
import kotlin.math.floor import kotlin.math.floor
import kotlin.random.Random
class YSP(var context: Context) { class YSP(var context: Context) {
private var cnlid = "" private var cnlid = ""
@@ -87,14 +89,8 @@ class YSP(var context: Context) {
fun generateGuid(): String { fun generateGuid(): String {
val timestamp = (System.currentTimeMillis()).toString(36) val timestamp = (System.currentTimeMillis()).toString(36)
val originalString = Math.random().toString() val randomPart = Random.nextLong().toString(36).take(11)
val resultString = if (originalString.startsWith("0.")) { return timestamp + "_" + "0".repeat(11 - randomPart.length) + randomPart
originalString.substring(2)
} else {
originalString
}
val randomPart = resultString.toLong().toString(36)
return timestamp + "_" + randomPart
} }
fun getGuid(): String { fun getGuid(): String {
@@ -10,7 +10,6 @@ import androidx.media3.common.MediaItem
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.hls.HlsMediaSource import androidx.media3.exoplayer.hls.HlsMediaSource
import com.lizongying.mytv.CustomLoadErrorHandlingPolicy
import com.lizongying.mytv.TV import com.lizongying.mytv.TV
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
import java.util.Date import java.util.Date
@@ -426,9 +425,7 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val httpDataSource = DefaultHttpDataSource.Factory() val httpDataSource = DefaultHttpDataSource.Factory()
mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) } mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) }
return HlsMediaSource.Factory(httpDataSource).setLoadErrorHandlingPolicy( return HlsMediaSource.Factory(httpDataSource).createMediaSource(
CustomLoadErrorHandlingPolicy(mMinimumLoadableRetryCount)
).createMediaSource(
MediaItem.fromUri( MediaItem.fromUri(
Uri.parse(videoUrl) Uri.parse(videoUrl)
) )
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

+3
View File
@@ -4,11 +4,14 @@
android:id="@+id/player_fragment" android:id="@+id/player_fragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black"
android:keepScreenOn="true"> android:keepScreenOn="true">
<androidx.media3.ui.PlayerView <androidx.media3.ui.PlayerView
android:id="@+id/player_view" android:id="@+id/player_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center"
app:resize_mode="fill"
app:use_controller="false" /> app:use_controller="false" />
</FrameLayout> </FrameLayout>
+1
View File
@@ -1,3 +1,4 @@
<resources> <resources>
<color name="fastlane_background">#30000000</color> <color name="fastlane_background">#30000000</color>
<color name="black">#000</color>
</resources> </resources>
+1 -1
View File
@@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">My TV</string> <string name="app_name">我的电视</string>
</resources> </resources>
-1
View File
@@ -5,5 +5,4 @@
<item name="browsePaddingEnd">11dp</item> <item name="browsePaddingEnd">11dp</item>
<item name="browseRowsMarginTop">54dp</item> <item name="browseRowsMarginTop">54dp</item>
</style> </style>
</resources> </resources>
+1 -1
View File
@@ -21,5 +21,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true #android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false android.nonFinalResIds=false