channel filter

This commit is contained in:
Li ZongYing
2024-03-26 13:36:26 +08:00
parent 8290a86909
commit 65c4b0cf52
26 changed files with 233 additions and 1234 deletions
@@ -27,12 +27,12 @@ class CardPresenter(
val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView
cardView.titleText = tvViewModel.title.value
cardView.titleText = tvViewModel.getTV().title
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
cardView.tag = tvViewModel.videoUrl.value
Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.load(tvViewModel.getTV().logo)
.centerInside()
.into(cardView.mainImageView)
@@ -30,7 +30,7 @@ class ChannelFragment : Fragment() {
fun show(tvViewModel: TVViewModel) {
handler.removeCallbacks(hideRunnable)
handler.removeCallbacks(playRunnable)
binding.channelContent.text = (tvViewModel.id.value?.plus(1)).toString()
binding.channelContent.text = (tvViewModel.getTV().id.plus(1)).toString()
view?.visibility = View.VISIBLE
handler.postDelayed(hideRunnable, delay)
}
@@ -1,157 +0,0 @@
package com.lizongying.mytv
import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
/**
*@author LeGend
*@date 2024/2/4 22:42
*/
object ChannelUtils {
/**
* 获取服务器channel版本
*
* @param context Context
*
* @return 服务器channel版本
*/
suspend fun getServerVersion(context: Context): Int {
return withContext(Dispatchers.IO) {
val client = okhttp3.OkHttpClient.Builder().connectTimeout(500, java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(1, java.util.concurrent.TimeUnit.SECONDS).build()
client.newCall(okhttp3.Request.Builder().url(getServerVersionUrl(context)).build()).execute()
.use { response ->
if (!response.isSuccessful) throw java.io.IOException("Unexpected code $response")
val body = response.body()
body?.string()?.toInt() ?: 0
}
}
}
/**
* 获取服务器channel
*
* @param url String 服务器地址
*
* @return Array<TV> 服务器channel
*
* @throws java.io.IOException 网络请求失败
*/
suspend fun getServerChannel(url: String): List<TV> {
val result = withContext(Dispatchers.IO) {
val client = okhttp3.OkHttpClient.Builder().connectTimeout(500, java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(1, java.util.concurrent.TimeUnit.SECONDS).build()
val request = okhttp3.Request.Builder().url(url).build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw java.io.IOException("Unexpected code $response")
val body = response.body()
body?.string() ?: ""
}
}
return withContext(Dispatchers.Default) {
val type = object : com.google.gson.reflect.TypeToken<List<TV>>() {}.type
com.google.gson.Gson().fromJson(result, type)
}
}
/**
* 获取服务器地址
*
* @param context Context
*
* @return 服务器地址
*/
fun getServerUrl(context: Context): String {
return context.resources.getString(R.string.server_url)
}
/**
* 获取serverVersion的URL
*
* @param context Context
*
* @return serverVersionURL 服务器版本地址
*/
suspend fun getServerVersionUrl(context: Context): String {
return withContext(Dispatchers.IO) {
context.resources.getString(R.string.server_version_url)
}
}
/**
* 获取本地channel版本
*
* @param context Context
*
* @return 本地channel
*/
suspend fun getLocalVersion(context: Context): Int {
return withContext(Dispatchers.IO) {
val file = File(getAppDirectory(context), "channels")
//检查本地是否已经有保存的channels.json,若无保存的Channel.json则从读取assert中文件
val savedVersion =
context.getSharedPreferences("saved_version", Context.MODE_PRIVATE).getInt("version", Integer.MIN_VALUE)
if (!file.exists() || savedVersion == Integer.MIN_VALUE) {
context.resources.getInteger(R.integer.local_channel_version)
} else {
savedVersion
}
}
}
/**
* 获取本地可读取的目录
* @param context Context
*
* @return 可读取的目录
*/
private fun getAppDirectory(context: Context): File {
return context.filesDir
}
/**
* 获取本地channel
*
* @param context Context
*
* @return Array<TV> 本地channel
*/
suspend fun getLocalChannel(context: Context): List<TV> {
val str = withContext(Dispatchers.IO) {
if (File(getAppDirectory(context), "channels").exists()) {
File(getAppDirectory(context), "channels").readText()
} else {
context.resources.openRawResource(R.raw.channels).bufferedReader().use { it.readText() }
}
}
return withContext(Dispatchers.Default) {
val type = object : com.google.gson.reflect.TypeToken<List<TV>>() {}.type
com.google.gson.Gson().fromJson(str, type)
}
}
/**
* 更新channels.json
*
* @param context Context
*
* @return 无
*
* @throws java.io.IOException 写入失败
*/
suspend fun updateLocalChannel(context: Context, version: Int, channels: List<TV>) {
withContext(Dispatchers.IO) {
val file = File(getAppDirectory(context), "channels")
if (!file.exists()) {
file.createNewFile()
}
file.writeText(com.google.gson.Gson().toJson(channels))
context.getSharedPreferences("saved_version", Context.MODE_PRIVATE).edit().putInt(
"version", version
).apply()
}
}
}
@@ -0,0 +1,28 @@
package com.lizongying.mytv
import android.view.View
import androidx.core.content.ContextCompat
import androidx.leanback.app.ErrorSupportFragment
class ErrorFragment : ErrorSupportFragment() {
internal fun setErrorContent(message: String) {
imageDrawable =
ContextCompat.getDrawable(context!!, androidx.leanback.R.drawable.lb_ic_sad_cloud)
this.message = message
setDefaultBackground(TRANSLUCENT)
backgroundDrawable = ContextCompat.getDrawable(
context!!,
R.color.black
)
buttonText = resources.getString(R.string.dismiss_error)
buttonClickListener = View.OnClickListener {
fragmentManager!!.beginTransaction().remove(this@ErrorFragment).commit()
}
}
companion object {
private const val TRANSLUCENT = false
}
}
@@ -7,6 +7,8 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.content.pm.SigningInfo
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.util.Log
import java.security.MessageDigest
@@ -71,6 +73,25 @@ val Context.appSignature: String
return hashSignature(sign)
}
val Context.isNetworkConnected: Boolean
get() {
val connectivityManager =
this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
return networkCapabilities != null &&
(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET))
} else {
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.isConnectedOrConnecting
}
}
private fun hashSignature(signature: Signature): String {
return try {
val md = MessageDigest.getInstance("MD5")
@@ -29,13 +29,13 @@ class InfoFragment : Fragment() {
}
fun show(tvViewModel: TVViewModel) {
binding.textView.text = tvViewModel.title.value
binding.textView.text = tvViewModel.getTV().title
Glide.with(this)
.load(tvViewModel.logo.value)
.load(tvViewModel.getTV().logo)
.into(binding.infoLogo)
Log.i(TAG, "${tvViewModel.title.value} ${tvViewModel.epg.value}")
Log.i(TAG, "${tvViewModel.getTV().title} ${tvViewModel.epg.value}")
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) {
binding.infoDesc.text = epg.last().title
@@ -1,5 +1,9 @@
package com.lizongying.mytv
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -7,11 +11,17 @@ import android.util.Log
import android.view.GestureDetector
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
import android.view.WindowManager
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.models.TVViewModel
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
class MainActivity : FragmentActivity(), Request.RequestListener {
@@ -22,6 +32,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private val infoFragment = InfoFragment()
private val channelFragment = ChannelFragment()
private val settingFragment = SettingFragment()
private val errorFragment = ErrorFragment()
private var doubleBackToExitPressedOnce = false
@@ -31,6 +42,16 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private val delayHideMain: Long = 10000
private val delayHideSetting: Long = 10000
init {
lifecycleScope.launch(Dispatchers.IO) {
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
utilsJob.start()
// utilsJob.await()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState)
@@ -50,10 +71,38 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
.add(R.id.main_browse_fragment, infoFragment)
.add(R.id.main_browse_fragment, channelFragment)
.add(R.id.main_browse_fragment, mainFragment)
// .add(R.id.main_browse_fragment, errorFragment)
.hide(mainFragment)
// .hide(errorFragment)
.commit()
}
gestureDetector = GestureDetector(this, GestureListener())
errorFragment.buttonClickListener = View.OnClickListener {
supportFragmentManager.beginTransaction()
.remove(errorFragment)
.commit()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val connectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(object :
ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
if (this@MainActivity.isNetworkConnected) {
Log.i(TAG, "net isNetworkConnected")
ready++
}
}
})
} else {
Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
ready++
}
}
fun showInfoFragment(tvViewModel: TVViewModel) {
@@ -146,11 +195,19 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun fragmentReady() {
ready++
Log.i(TAG, "ready $ready")
if (ready == 5) {
if (ready == 6) {
mainFragment.fragmentReady()
}
}
fun isPlaying() {
if (errorFragment.isVisible) {
supportFragmentManager.beginTransaction()
.remove(errorFragment)
.commit()
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event != null) {
gestureDetector.onTouchEvent(event)
@@ -442,7 +499,13 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
Request.onDestroy()
}
override fun onRequestFinished() {
override fun onRequestFinished(message: String?) {
if (message != null && !errorFragment.isVisible) {
supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, errorFragment)
.commitNow()
errorFragment.setErrorContent(message)
}
fragmentReady()
}
@@ -1,13 +1,12 @@
package com.lizongying.mytv
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.leanback.app.BrowseSupportFragment
import androidx.leanback.widget.ArrayObjectAdapter
import androidx.leanback.widget.HeaderItem
import androidx.leanback.widget.ImageCardView
import androidx.leanback.widget.ListRow
import androidx.leanback.widget.ListRowPresenter
import androidx.leanback.widget.ListRowPresenter.SelectItemViewHolderTask
@@ -17,7 +16,6 @@ import androidx.leanback.widget.Presenter
import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter
import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel
@@ -57,7 +55,7 @@ class MainFragment : BrowseSupportFragment() {
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.errInfo.value != null
&& tvViewModel.id.value == itemPosition
&& tvViewModel.getTV().id == itemPosition
) {
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT).show()
}
@@ -66,18 +64,18 @@ class MainFragment : BrowseSupportFragment() {
// not first time && channel not change
if (tvViewModel.ready.value != null
&& tvViewModel.id.value == itemPosition
&& tvViewModel.getTV().id == itemPosition
&& check(tvViewModel)
) {
Log.i(TAG, "ready ${tvViewModel.title.value}")
Log.i(TAG, "ready ${tvViewModel.getTV().title}")
(activity as? MainActivity)?.play(tvViewModel)
}
}
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) {
val title = tvViewModel.title.value
val title = tvViewModel.getTV().title
Log.i(TAG, "switch $title")
if (tvViewModel.pid.value != "") {
if (tvViewModel.getTV().pid != "") {
Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { Request.fetchData(it) }
@@ -196,8 +194,8 @@ class MainFragment : BrowseSupportFragment() {
row: Row
) {
if (item is TVViewModel) {
if (itemPosition != item.id.value!!) {
itemPosition = item.id.value!!
if (itemPosition != item.getTV().id) {
itemPosition = item.getTV().id
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
@@ -211,15 +209,18 @@ class MainFragment : BrowseSupportFragment() {
itemViewHolder: Presenter.ViewHolder?, item: Any?,
rowViewHolder: RowPresenter.ViewHolder, row: Row
) {
// if (itemViewHolder !=null) {
// (itemViewHolder.view as ImageCardView).setInfoAreaBackgroundColor(resources.getColor(R.color.focus))
// }
if (item is TVViewModel) {
tvListViewModel.setItemPositionCurrent(item.id.value!!)
tvListViewModel.setItemPositionCurrent(item.getTV().id)
(activity as MainActivity).mainActive()
}
}
}
fun check(tvViewModel: TVViewModel): Boolean {
val title = tvViewModel.title.value
val title = tvViewModel.getTV().title
val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
if (videoUrl == null || videoUrl == "") {
Log.e(TAG, "$title videoUrl is empty")
@@ -0,0 +1,16 @@
package com.lizongying.mytv
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
class NetworkChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
val isConnected = context?.isNetworkConnected
if (isConnected == true) {
}
}
}
}
@@ -82,6 +82,13 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
Log.e(TAG, "PlaybackException $error")
tvViewModel?.changed()
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
if (isPlaying) {
(activity as MainActivity).isPlaying()
}
}
})
}
})
@@ -24,6 +24,9 @@ import com.lizongying.mytv.api.YSPTokenService
import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
@@ -57,7 +60,7 @@ object Request {
private var listener: RequestListener? = null
private var initRetryTimes = 0
private var initRetryMaxTimes = 1
private var initRetryMaxTimes = 0
fun onCreate() {
Log.i(TAG, "onCreate")
@@ -86,7 +89,7 @@ object Request {
private fun fetchAuth(tvModel: TVViewModel, cookie: String) {
cancelCall()
val title = tvModel.title.value
val title = tvModel.getTV().title
val data = YSP.getAuthData(tvModel)
val request = AuthRequest(data)
@@ -166,17 +169,24 @@ object Request {
handler.removeCallbacks(btraceRunnable)
}
val title = tvModel.title.value
val title = tvModel.getTV().title
tvModel.seq = 0
val data = YSP.switch(tvModel)
val request = LiveInfoRequest(data)
call = request.let {
yspApiService.getLiveInfo(
"guid=${YSP.getGuid()}; $cookie",
YSP.token,
it
)
if (token == "") {
yspApiService.getLiveInfoV2(
"guid=${YSP.getGuid()}; $cookie",
it,
)
} else {
yspApiService.getLiveInfo(
"guid=${YSP.getGuid()}; $cookie",
YSP.token,
it
)
}
}
call?.enqueue(object : Callback<LiveInfo> {
@@ -413,7 +423,7 @@ object Request {
private fun fetchFAuth(tvModel: TVViewModel) {
cancelCall()
val title = tvModel.title.value
val title = tvModel.getTV().title
var qa = "HD"
if (tokenFH != "") {
@@ -455,6 +465,9 @@ object Request {
}
if (tvModel.getTV().needToken) {
if (token == "") {
tvModel.needGetToken = true
}
if (needAuth) {
fetchAuth(tvModel)
} else {
@@ -462,7 +475,7 @@ object Request {
}
} else {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205"
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; updateProtocol=1; deviceModel=120"
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
@@ -516,7 +529,7 @@ object Request {
Utils.setBetween(c * 1000L)
Log.i(TAG, "current time $c")
}
listener?.onRequestFinished()
listener?.onRequestFinished(null)
} else {
Log.e(TAG, "token status error")
handler.postDelayed(
@@ -527,7 +540,7 @@ object Request {
initRetryTimes++
fetchInfoV2()
} else {
listener?.onRequestFinished()
listener?.onRequestFinished("状态错误")
}
}
}
@@ -543,7 +556,7 @@ object Request {
initRetryTimes++
fetchInfoV2()
} else {
listener?.onRequestFinished()
listener?.onRequestFinished("网络错误")
}
}
})
@@ -605,11 +618,11 @@ object Request {
}
fun fetchBtrace(tvModel: TVViewModel) {
val title = tvModel.title.value
val title = tvModel.getTV().title
val guid = YSP.getGuid()
val pid = tvModel.pid.value!!
val sid = tvModel.sid.value!!
val pid = tvModel.getTV().pid
val sid = tvModel.getTV().sid
yspBtraceService.kvcollect(
c_timestamp = YSP.generateGuid(),
guid = guid,
@@ -675,8 +688,8 @@ object Request {
}
fun fetchYEPG(tvViewModel: TVViewModel) {
val title = tvViewModel.title.value
yspProtoService.getProgram(tvViewModel.programId.value!!, getDateFormat("yyyyMMdd"))
val title = tvViewModel.getTV().title
yspProtoService.getProgram(tvViewModel.getTV().programId, getDateFormat("yyyyMMdd"))
.enqueue(object : Callback<epgProgramModel.Response> {
override fun onResponse(
call: Call<epgProgramModel.Response>,
@@ -700,8 +713,8 @@ object Request {
}
fun fetchFEPG(tvViewModel: TVViewModel) {
val title = tvViewModel.title.value
fAuthService.getEPG(tvViewModel.pid.value!!, getDateFormat("yyyyMMdd"))
val title = tvViewModel.getTV().title
fAuthService.getEPG(tvViewModel.getTV().pid, getDateFormat("yyyyMMdd"))
.enqueue(object : Callback<List<FEPG>> {
override fun onResponse(
call: Call<List<FEPG>>,
@@ -738,7 +751,7 @@ object Request {
}
interface RequestListener {
fun onRequestFinished()
fun onRequestFinished(message: String?)
}
fun setRequestListener(listener: RequestListener) {
@@ -5,12 +5,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import com.lizongying.mytv.databinding.DialogBinding
import com.lizongying.mytv.databinding.SettingBinding
class SettingFragment : DialogFragment() {
private var _binding: DialogBinding? = null
private var _binding: SettingBinding? = null
private val binding get() = _binding!!
private lateinit var updateManager: UpdateManager
@@ -26,7 +26,7 @@ class SettingFragment : DialogFragment() {
savedInstanceState: Bundle?
): View {
val context = requireContext() // It‘s safe to get context here.
_binding = DialogBinding.inflate(inflater, container, false)
_binding = SettingBinding.inflate(inflater, container, false)
binding.versionName.text = "当前版本: v${context.appVersionName}"
binding.version.text = "https://github.com/lizongying/my-tv"
@@ -76,11 +76,6 @@ class SettingFragment : DialogFragment() {
_binding = null
}
override fun onDestroy() {
super.onDestroy()
// updateManager.destroy()
}
companion object {
const val TAG = "SettingFragment"
}
@@ -1,12 +1,5 @@
package com.lizongying.mytv
import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
object TVList {
val list: Map<String, List<TV>> by lazy {
setup()
@@ -967,66 +960,7 @@ object TVList {
}
}
val array = arrayOf("央视", "地方")
// list = list.filterKeys { it in array }
list = list.filterKeys { it in array }
return list
}
/**
* 初始化
*
* @param context Context
*/
fun init(context: Context) {
CoroutineScope(Dispatchers.Default).launch {
//获取本地版本号
val localVersion = ChannelUtils.getLocalVersion(context)
//获取服务器版本号
val serverVersion = try {
ChannelUtils.getServerVersion(context)
} catch (e: IOException) {
Log.e("TVList", "无法从服务器获取版本信息", e)
Integer.MIN_VALUE
}
//频道列表
val channelTVMap: MutableMap<String, MutableList<TV>> = mutableMapOf()
//是否从服务器更新
var updateFromServer = false
//获取频道列表
val tvList: List<TV> = if (localVersion < serverVersion) {
//获取服务器地址
val url = ChannelUtils.getServerUrl(context)
//是否从服务器更新
updateFromServer = true
Log.i("TVList", "从服务器获取频道信息")
try {
ChannelUtils.getServerChannel(url)
} catch (e: IOException) {
Log.e("TVList", "无法从服务器获取频道信息", e)
updateFromServer = false
ChannelUtils.getLocalChannel(context)
}
} else {
Log.i("TVList", "从本地获取频道信息")
//获取本地频道
ChannelUtils.getLocalChannel(context)
}
//按频道分类
for (tv in tvList) {
val key = tv.channel
if (channelTVMap.containsKey(key)) {
val list = channelTVMap[key]!!
list.add(tv)
channelTVMap[key] = list
} else {
channelTVMap[key] = mutableListOf(tv)
}
}
//保存频道列表
// list = channelTVMap
//保存版本号
if (updateFromServer) {
ChannelUtils.updateLocalChannel(context, serverVersion, tvList)
}
}
}
}
@@ -9,6 +9,8 @@ import okhttp3.TlsVersion
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.protobuf.ProtoConverterFactory
import java.net.InetSocketAddress
import java.net.Proxy
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
@@ -1,6 +1,7 @@
package com.lizongying.mytv.api
import android.content.Context
import android.util.Log
import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.SP
@@ -63,8 +64,8 @@ object YSP {
}
fun switch(tvModel: TVViewModel): String {
livepid = tvModel.pid.value!!
cnlid = tvModel.sid.value!!
livepid = tvModel.getTV().pid
cnlid = tvModel.getTV().sid
defn = "fhd"
randStr = getRand()
@@ -82,7 +83,7 @@ object YSP {
}
fun getAuthData(tvModel: TVViewModel): String {
livepid = tvModel.pid.value!!
livepid = tvModel.getTV().pid
randStr = getRand()
@@ -21,6 +21,18 @@ interface YSPApiService {
@Body request: LiveInfoRequest,
): Call<LiveInfo>
@POST("v1/player/get_live_info")
@Headers(
"content-type: application/json;charset=UTF-8",
"referer: https://www.yangshipin.cn/",
"yspappid: 519748109",
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
fun getLiveInfoV2(
@Header("cookie") cookie: String,
@Body request: LiveInfoRequest,
): Call<LiveInfo>
@POST("v1/player/auth")
@Headers(
"content-type: application/x-www-form-urlencoded;charset=UTF-8",
@@ -1,15 +1,9 @@
package com.lizongying.mytv.models
import android.net.Uri
import android.util.Log
import androidx.annotation.OptIn
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.hls.HlsMediaSource
import com.lizongying.mytv.TV
import com.lizongying.mytv.api.FEPG
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
@@ -26,7 +20,7 @@ class TVViewModel(private var tv: TV) : ViewModel() {
var tokenYSPRetryTimes = 0
var tokenYSPRetryMaxTimes = 0
var tokenFHRetryTimes = 0
var tokenFHRetryMaxTimes = 4
var tokenFHRetryMaxTimes = 8
var needGetToken = false
@@ -34,22 +28,10 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val errInfo: LiveData<String>
get() = _errInfo
private val _programId = MutableLiveData<String>()
val programId: LiveData<String>
get() = _programId
private var _epg = MutableLiveData<MutableList<EPG>>()
val epg: LiveData<MutableList<EPG>>
get() = _epg
private val _id = MutableLiveData<Int>()
val id: LiveData<Int>
get() = _id
private val _title = MutableLiveData<String>()
val title: LiveData<String>
get() = _title
private val _videoUrl = MutableLiveData<List<String>>()
val videoUrl: LiveData<List<String>>
get() = _videoUrl
@@ -58,18 +40,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val videoIndex: LiveData<Int>
get() = _videoIndex
private val _logo = MutableLiveData<Any>()
val logo: LiveData<Any>
get() = _logo
private val _pid = MutableLiveData<String>()
val pid: LiveData<String>
get() = _pid
private val _sid = MutableLiveData<String>()
val sid: LiveData<String>
get() = _sid
private val _change = MutableLiveData<Boolean>()
val change: LiveData<Boolean>
get() = _change
@@ -116,14 +86,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
}
init {
_id.value = tv.id
_title.value = tv.title
_videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoUrl.lastIndex
_logo.value = tv.logo
_programId.value = tv.programId
_pid.value = tv.pid
_sid.value = tv.sid
}
fun getRowPosition(): Int {
@@ -146,10 +110,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
_errInfo.value = info
}
fun update(t: TV) {
tv = t
}
fun getTV(): TV {
return tv
}
@@ -172,21 +132,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
_epg.value = p.map { EPG(it.title, formatFTime(it.event_time)) }.toMutableList()
}
/**
* (playerView?.player as ExoPlayer).setMediaSource(tvViewModel.buildSource())
*/
@OptIn(UnstableApi::class)
fun buildSource(): HlsMediaSource {
val httpDataSource = DefaultHttpDataSource.Factory()
// mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) }
return HlsMediaSource.Factory(httpDataSource).createMediaSource(
MediaItem.fromUri(
Uri.parse(getVideoUrlCurrent())
)
)
}
fun getVideoUrlCurrent(): String {
return _videoUrl.value!![_videoIndex.value!!]
}