replay on resume

This commit is contained in:
Li ZongYing
2024-02-01 18:10:58 +08:00
parent 4bdc625718
commit dd4550cf4f
14 changed files with 243 additions and 105 deletions
+17 -5
View File
@@ -1,6 +1,6 @@
# 我的电视
安卓电视直播软件,内置直播源
电视直播软件,安装即可使用
## 使用
@@ -14,16 +14,28 @@
## 更新日志
### v1.5.1(高版本专用)
* 性能优化
### v1.5.0(通用版)
* 修复部分情况下APP切换后无法继续播放的问题
### v1.4.9(高版本专用)
* 同步v1.4.8
### v1.4.8(通用版)
* 频道号从1开始,cctv5+为18
* 提高cctv6清晰度
* 频道号从1开始,CCTV5+为18
* 提高CCTV6清晰度
* 增加天津卫视、新疆卫视
### v1.4.7(高版本专用)
* 修复部分用户cctv13播放过程中卡住的问题
* 调整cctv的频道顺序
* 修复部分用户CCTV13播放过程中卡住的问题
* 调整CCTV的频道顺序
### v1.4.6(通用版)
+5 -3
View File
@@ -9,17 +9,19 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:largeHeap="true"
android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:banner="@drawable/banner"
android:label="@string/app_name"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.MyTV">
<activity
android:keepScreenOn="true"
android:name=".MainActivity"
android:banner="@drawable/banner"
android:exported="true"
android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Binary file not shown.
Binary file not shown.
@@ -48,7 +48,6 @@ class InfoFragment : Fragment() {
.into(binding.infoLogo)
}
val program = tvViewModel.getProgramOne()
if (program != null) {
binding.infoDesc.text = program.name
@@ -35,16 +35,17 @@ class MainActivity : FragmentActivity() {
private lateinit var gestureDetector: GestureDetector
private val handler = Handler()
private val delay: Long = 4000
private val delayHideHelp: Long = 10000
private val delayHideMain: Long = 5000
private val delayHideSetting: Long = 10000
private lateinit var sharedPref: SharedPreferences
lateinit var sharedPref: SharedPreferences
private var channelReversal = false
private var channelNum = true
private var versionName = ""
override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
@@ -60,7 +61,6 @@ class MainActivity : FragmentActivity() {
.add(R.id.main_browse_fragment, mainFragment)
.hide(mainFragment)
.commit()
mainFragment.view?.requestFocus()
}
gestureDetector = GestureDetector(this, GestureListener())
@@ -133,11 +133,11 @@ class MainActivity : FragmentActivity() {
}
fun keepRunnable() {
handler.removeCallbacks(hideRunnable)
handler.postDelayed(hideRunnable, delay)
handler.removeCallbacks(hideMain)
handler.postDelayed(hideMain, delayHideMain)
}
private val hideRunnable = Runnable {
private val hideMain = Runnable {
if (!mainFragment.isHidden) {
supportFragmentManager.beginTransaction().hide(mainFragment).commit()
}
@@ -223,7 +223,7 @@ class MainActivity : FragmentActivity() {
this.channelNum = channelNum
}
private fun showHelp() {
private fun showSetting() {
if (!mainFragment.isHidden) {
return
}
@@ -231,15 +231,15 @@ class MainActivity : FragmentActivity() {
Log.i(TAG, "settingFragment ${settingFragment.isVisible}")
if (!settingFragment.isVisible) {
settingFragment.show(supportFragmentManager, "setting")
handler.removeCallbacks(hideHelp)
handler.postDelayed(hideHelp, delayHideHelp)
handler.removeCallbacks(hideSetting)
handler.postDelayed(hideSetting, delayHideSetting)
} else {
handler.removeCallbacks(hideHelp)
handler.removeCallbacks(hideSetting)
settingFragment.dismiss()
}
}
private val hideHelp = Runnable {
private val hideSetting = Runnable {
if (settingFragment.isVisible) {
settingFragment.dismiss()
}
@@ -360,27 +360,27 @@ class MainActivity : FragmentActivity() {
}
KeyEvent.KEYCODE_BOOKMARK -> {
showHelp()
showSetting()
return true
}
KeyEvent.KEYCODE_UNKNOWN -> {
showHelp()
showSetting()
return true
}
KeyEvent.KEYCODE_HELP -> {
showHelp()
showSetting()
return true
}
KeyEvent.KEYCODE_SETTINGS -> {
showHelp()
showSetting()
return true
}
KeyEvent.KEYCODE_MENU -> {
showHelp()
showSetting()
return true
}
@@ -478,7 +478,7 @@ class MainActivity : FragmentActivity() {
private fun hashSignature(signature: Signature): String {
return try {
val md = MessageDigest.getInstance("SHA-256")
val md = MessageDigest.getInstance("MD5")
md.update(signature.toByteArray())
val digest = md.digest()
digest.let { it -> it.joinToString("") { "%02x".format(it) } }
@@ -488,6 +488,22 @@ class MainActivity : FragmentActivity() {
}
}
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onPause() {
Log.i(TAG, "onPause")
super.onPause()
handler.removeCallbacks(hideMain)
}
companion object {
private const val TAG = "MainActivity"
private const val CHANNEL_REVERSAL = "channel_reversal"
@@ -1,6 +1,5 @@
package com.lizongying.mytv
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.os.Handler
@@ -36,7 +35,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
var tvListViewModel = TVListViewModel()
private var sharedPref: SharedPreferences? = null
private lateinit var sharedPref: SharedPreferences
private var lastVideoUrl = ""
@@ -57,7 +56,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
super.onActivityCreated(savedInstanceState)
activity?.let { request.initYSP(it) }
sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
sharedPref = (activity as? MainActivity)?.sharedPref!!
view?.post {
val content = binding.content
@@ -98,13 +97,13 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
mUpdateProgramRunnable = UpdateProgramRunnable()
handler.post(mUpdateProgramRunnable)
itemPosition = sharedPref?.getInt(POSITION, 0)!!
itemPosition = sharedPref.getInt(POSITION, 0)
if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
setPosition()
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel ->
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.errInfo.value != null
&& tvViewModel.id.value == itemPosition
@@ -262,7 +261,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
} else {
if (timestamp - tvViewModel.program.value!!.last().et < 600) {
if (tvViewModel.program.value!!.last().et - timestamp < 600) {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
}
@@ -272,7 +271,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
inner class UpdateProgramRunnable : Runnable {
override fun run() {
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null }
tvListViewModel.tvListViewModel.value?.filter { it.programId.value != null }
?.forEach { tvViewModel ->
updateProgram(
tvViewModel
@@ -282,14 +281,19 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
}
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onStop() {
Log.i(TAG, "onStop")
super.onStop()
with(sharedPref!!.edit()) {
with(sharedPref.edit()) {
putInt(POSITION, itemPosition)
apply()
}
Log.i(TAG, "POSITION saved")
Log.i(TAG, "$POSITION saved")
}
override fun onDestroy() {
@@ -304,11 +308,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
_binding = null
}
override fun onResume() {
super.onResume()
view?.post { view?.requestFocus() }
}
companion object {
private const val TAG = "MainFragment"
private const val POSITION = "position"
@@ -74,23 +74,31 @@ class PlayerFragment : Fragment() {
@OptIn(UnstableApi::class)
fun play(tvViewModel: TVViewModel) {
this.tvViewModel = tvViewModel
val videoUrlCurrent = tvViewModel.getVideoUrlCurrent()
playerView?.player?.run {
setMediaItem(MediaItem.fromUri(videoUrlCurrent))
setMediaItem(MediaItem.fromUri(tvViewModel.getVideoUrlCurrent()))
prepare()
}
}
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
if (playerView != null) {
playerView!!.player?.play()
if (playerView != null && playerView!!.player?.isPlaying == false) {
Log.i(TAG, "replay")
playerView!!.player?.prepare()
} else {
Log.i(TAG, "playing")
}
}
override fun onStop() {
super.onStop()
if (playerView != null) {
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onPause() {
super.onPause()
if (playerView != null && playerView!!.player?.isPlaying == true) {
playerView!!.player?.stop()
}
}
@@ -32,10 +32,12 @@ class Request {
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
private var ysp: YSP? = null
private var token = ""
// TODO onDestroy
private val handler = Handler(Looper.getMainLooper())
private lateinit var myRunnable: MyRunnable
private lateinit var btraceRunnable: BtraceRunnable
private var tokenRunnable: TokenRunnable = TokenRunnable()
private var mapping = mapOf(
"CCTV4K" to "CCTV4K 超高清",
@@ -85,6 +87,10 @@ class Request {
"新疆卫视" to "新疆卫视",
)
init {
handler.post(tokenRunnable)
}
fun initYSP(context: Context) {
ysp = YSP(context)
}
@@ -93,8 +99,8 @@ class Request {
fun fetchVideo(tvModel: TVViewModel, cookie: String) {
call?.cancel()
if (::myRunnable.isInitialized) {
handler.removeCallbacks(myRunnable)
if (::btraceRunnable.isInitialized) {
handler.removeCallbacks(btraceRunnable)
}
val title = tvModel.title.value
@@ -125,19 +131,24 @@ class Request {
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
ivBytes
).uppercase()
Log.i(TAG, "$title url $url")
// Log.d(TAG, "$title url $url")
tvModel.addVideoUrl(url)
tvModel.allReady()
tvModel.retryTimes = 0
myRunnable = MyRunnable(tvModel)
handler.post(myRunnable)
btraceRunnable = BtraceRunnable(tvModel)
handler.post(btraceRunnable)
} else {
Log.e(TAG, "$title key error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
if (tvModel.needToken) {
token = ""
fetchVideo(tvModel)
} else {
fetchVideo(tvModel, cookie)
}
}
}
} else {
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
Log.e(TAG, "$title error ${liveInfo.data.errinfo}")
@@ -146,35 +157,51 @@ class Request {
Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
if (tvModel.needToken) {
token = ""
fetchVideo(tvModel)
} else {
fetchVideo(tvModel, cookie)
}
}
}
}
} else {
Log.e(TAG, "$title status error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
if (tvModel.needToken) {
token = ""
fetchVideo(tvModel)
} else {
fetchVideo(tvModel, cookie)
}
}
}
}
override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
Log.e(TAG, "$title request error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
if (tvModel.needToken) {
token = ""
fetchVideo(tvModel)
} else {
fetchVideo(tvModel, cookie)
}
}
}
})
}
fun fetchVideo(tvModel: TVViewModel) {
if (token == "") {
yspTokenService.getInfo()
.enqueue(object : Callback<Info> {
override fun onResponse(call: Call<Info>, response: Response<Info>) {
if (response.isSuccessful) {
val token = response.body()?.data?.token
token = response.body()?.data?.token!!
Log.i(TAG, "info success $token")
val cookie =
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
@@ -196,6 +223,11 @@ class Request {
}
}
})
} else {
val cookie =
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
fetchVideo(tvModel, cookie)
}
}
fun fetchData(tvModel: TVViewModel) {
@@ -207,7 +239,32 @@ class Request {
}
}
inner class MyRunnable(private val tvModel: TVViewModel) : Runnable {
inner class TokenRunnable : Runnable {
override fun run() {
fetchToken()
handler.postDelayed(this, 600000)
}
}
fun fetchToken() {
yspTokenService.getInfo()
.enqueue(object : Callback<Info> {
override fun onResponse(call: Call<Info>, response: Response<Info>) {
if (response.isSuccessful) {
token = response.body()?.data?.token!!
Log.i(TAG, "info success $token")
} else {
Log.e(TAG, "token status error")
}
}
override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "token request error $t")
}
})
}
inner class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() {
fetchBtrace(tvModel)
handler.postDelayed(this, 60000)
@@ -304,8 +361,10 @@ class Request {
val program = response.body()
if (program != null) {
tvViewModel.addProgram(program.dataListList)
Log.i(TAG, "$title program ${program.dataListList.size}")
Log.d(TAG, "$title program ${program.dataListList.size}")
}
} else {
Log.w(TAG, "$title program error")
}
}
@@ -277,9 +277,6 @@ CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
}
if (!i.contains(",")) {
channel = i.trim()
if (channel == "移动专区") {
break
}
continue
}
val p = i.split(",")
@@ -2,6 +2,7 @@ package com.lizongying.mytv.api
import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.Utils.getDateTimestamp
@@ -51,14 +52,14 @@ class YSP(var context: Context) {
private var signature = ""
private var encryptor: Encryptor? = null
private var sharedPref: SharedPreferences? = null
private lateinit var sharedPref: SharedPreferences
init {
if (context is MainActivity) {
encryptor = Encryptor()
encryptor!!.init(context)
sharedPref = (context as MainActivity).getPreferences(Context.MODE_PRIVATE)
sharedPref = (context as MainActivity).sharedPref
}
guid = getGuid()
@@ -77,15 +78,10 @@ class YSP(var context: Context) {
timeStr = getTimeStr()
// guid = "lq3oqitm_1e15dnzgjnb"
// randStr = "BfcCPQp8Hq"
// timeStr = "1702166501"
cKey =
encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform)
signature = getSignature()
return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}"""
// return """{"cnlid":"2000203803","livepid":"600001801","stream":"2","guid":"lq1y36mb_ccfwmja9zan","cKey":"--01A9F5E89BB86A0C61F4025BEDE15309B6913A79FD1AF1EE7F5EC9C7605F377D1D2281488385C32DEB9E7D0DD3559CB700BD7AF44DD5C9DE0AE14D94B8214027B5D664C108AEE23532348DCC61B86F7C8FBB6CF14D588E6093A25E97DF6D66F4882AB28F17016472DD43D45EF076B7F505176A5E8DEDF2662E5F9AB12B69CB20BCE1579BE724091F3AF6826AE34B713906F3FE139C3783F80EECBD08416DC525E1","adjust":1,"sphttps":"1","platform":"5910204","cmd":"2","encryptVer":"8.1","dtype":"1","devid":"devid","otype":"ojson","appVer":"V1.0.0","app_version":"V1.0.0","rand_str":"IOS2soOw44","channel":"ysp_tx","defn":"fhd","signature":"6ca945f651817de8c6e6910457ceafd6"}"""
}
private fun getTimeStr(): String {
@@ -99,10 +95,10 @@ class YSP(var context: Context) {
}
fun getGuid(): String {
var guid = sharedPref?.getString("guid", "")
var guid = sharedPref.getString("guid", "")
if (guid == null || guid.length < 18) {
guid = generateGuid()
with(sharedPref!!.edit()) {
with(sharedPref.edit()) {
putString("guid", guid)
apply()
}
@@ -112,7 +108,7 @@ class YSP(var context: Context) {
private fun newGuid(): String {
guid = generateGuid()
with(sharedPref!!.edit()) {
with(sharedPref.edit()) {
putString("guid", guid)
apply()
}
@@ -8,7 +8,9 @@ class TVListViewModel : ViewModel() {
var maxNum = mutableListOf<Int>()
private val tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
private val _tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
val tvListViewModel: LiveData<MutableList<TVViewModel>>
get() = _tvListViewModel
private val _itemPosition = MutableLiveData<Int>()
val itemPosition: LiveData<Int>
@@ -18,22 +20,20 @@ class TVListViewModel : ViewModel() {
val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent
fun getTVListViewModel(): MutableLiveData<MutableList<TVViewModel>> {
return tvListViewModel
}
fun addTVViewModel(tvViewModel: TVViewModel) {
val currentTVModelList = tvListViewModel.value ?: mutableListOf()
currentTVModelList.add(tvViewModel)
tvListViewModel.value = currentTVModelList
if (_tvListViewModel.value == null) {
_tvListViewModel.value = mutableListOf(tvViewModel)
} else {
_tvListViewModel.value?.add(tvViewModel)
}
}
fun getTVViewModel(id: Int): TVViewModel? {
return tvListViewModel.value?.get(id)
return _tvListViewModel.value?.get(id)
}
fun getTVViewModelCurrent(): TVViewModel? {
return _itemPositionCurrent.value?.let { tvListViewModel.value?.get(it) }
return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) }
}
fun setItemPosition(position: Int) {
@@ -46,6 +46,9 @@ class TVListViewModel : ViewModel() {
}
fun size(): Int {
return tvListViewModel.value!!.size
if (_tvListViewModel.value == null) {
return 0
}
return _tvListViewModel.value!!.size
}
}
@@ -78,6 +78,20 @@ class TVViewModel(private var tv: TV) : ViewModel() {
var needToken = false
private val channelsNeedToken = arrayOf(
"CCTV4K 超高清",
"CCTV2 财经",
"CCTV5 体育",
"CCTV5+ 体育赛事",
"CCTV7 国防军事",
"CCTV9 记录",
"CCTV10 科教",
"CCTV11 戏曲",
"CCTV12 社会与法",
"CCTV14 少儿",
"CCTV15 音乐",
"CCTV16 奥林匹克",
"CCTV17 农业农村",
"CCTV3 综艺",
"CCTV6 电影",
"CCTV8 电视剧",
@@ -94,6 +108,30 @@ class TVViewModel(private var tv: TV) : ViewModel() {
"央视台球",
"电视指南",
"卫生健康",
"东方卫视",
"湖南卫视",
"湖北卫视",
"辽宁卫视",
"江苏卫视",
"江西卫视",
"山东卫视",
"广东卫视",
"广西卫视",
"重庆卫视",
"河南卫视",
"河北卫视",
"贵州卫视",
"北京卫视",
"黑龙江卫视",
"浙江卫视",
"安徽卫视",
"深圳卫视",
"四川卫视",
"东南卫视",
"海南卫视",
"天津卫视",
"新疆卫视",
)
fun addVideoUrl(url: String) {
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>