Compare commits

...
11 Commits
Author SHA1 Message Date
Li ZongYing 31988d0d86 fh fhd 2024-03-09 18:08:53 +08:00
Li ZongYing e55e8ceb18 delayHideMain 10s 2024-03-09 08:50:27 +08:00
Li ZongYing 0b62742350 token-v2 2024-03-08 22:50:47 +08:00
Li ZongYing 558fe7e4e7 cetv1 2024-03-08 21:17:13 +08:00
Li ZongYing e2145f8d0d bootStartup->false 2024-03-06 01:27:13 +08:00
Li ZongYing 46b4df1306 readme 2024-03-06 01:25:19 +08:00
李宗英 358539f9c3 Merge pull request #455 from WoooKong/main
创建 SP 工具类,管理所有(除 ChannelUtils 以外)用户偏好设置
2024-03-06 01:13:10 +08:00
WoooKong 17d8d4cb46 还原 VersionCode 2024-03-04 18:07:15 +08:00
WoooKong dc81112a3d Merge branch 'main' into main 2024-03-04 17:52:02 +08:00
WoooKong ede420a190 调整注释 2024-02-19 16:55:34 +08:00
WoooKong f94d136a03 创建 SP 工具类,管理所有(除 ChannelUtils 以外)用户偏好设置 2024-02-19 12:23:05 +08:00
19 changed files with 373 additions and 166 deletions
+2 -2
View File
@@ -19,8 +19,8 @@ body:
label: my-ty 版本 label: my-ty 版本
description: 请选择正在使用的版本 description: 请选择正在使用的版本
options: options:
- 最新稳定版 - 通用版
- 最新 CI 版 - 专用版
validations: validations:
required: true required: true
- type: textarea - type: textarea
+16 -4
View File
@@ -14,6 +14,20 @@
## 更新日志 ## 更新日志
### v1.6.4(通用)
* 增加CETV1
* 增加凤凰卫视
* 默认关闭开机启动
### v1.6.3(安卓5及以上专用)
* 增加CETV1
* 凤凰卫视增强画质
* 默认关闭开机启动
* 延迟菜单自动关闭时间
* 解决一些可能导致首次打开时黑屏的问题
### v1.6.2(通用) ### v1.6.2(通用)
* 修复按键无效的问题 * 修复按键无效的问题
@@ -199,17 +213,15 @@ adb install my-tv.apk
## TODO ## TODO
* 音量不同 * 音量不同
* 大湾区卫视、广东4k超高清、广东珠江 * 大湾区卫视、广东4k超高清、广东珠江、三沙卫视
* CETV教育频道
* CHC高清三个电影频道 * CHC高清三个电影频道
* 地方频道 * 地方频道
* 收藏夹 * 收藏夹
* 自定义源 * 自定义源
* 凤凰卫视、凤凰资讯台
* 海外 * 海外
* 1.5.0 无法安装,1.5.1 可以安装
* 节目增加预告 * 节目增加预告
* 频道列表优化 * 频道列表优化
* 自动更新
## 赞赏 ## 赞赏
+4
View File
@@ -38,5 +38,9 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<provider
android:name=".InitializerProvider"
android:authorities="${applicationId}.InitializerProvider"
android:exported="false"/>
</application> </application>
</manifest> </manifest>
@@ -7,8 +7,7 @@ import android.content.Intent
class BootReceiver : BroadcastReceiver() { class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val sp = context.getSharedPreferences("MainActivity", Context.MODE_PRIVATE) if (SP.bootStartup) {
if (sp.getBoolean(MainActivity.BOOT_STARTUP, true)) {
context.startActivity( context.startActivity(
Intent(context, MainActivity::class.java) Intent(context, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -47,6 +47,10 @@ class InfoFragment : Fragment() {
.load(R.drawable.bingtuan) .load(R.drawable.bingtuan)
.into(binding.infoLogo) .into(binding.infoLogo)
"CETV1" -> Glide.with(this)
.load(R.drawable.cetv1)
.into(binding.infoLogo)
else -> Glide.with(this) else -> Glide.with(this)
.load(tvViewModel.logo.value) .load(tvViewModel.logo.value)
.into(binding.infoLogo) .into(binding.infoLogo)
@@ -0,0 +1,39 @@
package com.lizongying.mytv
import android.content.ContentProvider
import android.content.ContentValues
import android.net.Uri
internal class InitializerProvider : ContentProvider() {
// Happens before Application#onCreate.It's fine to init something here
override fun onCreate(): Boolean {
SP.init(context!!)
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?,
) = unsupported()
override fun getType(uri: Uri) = unsupported()
override fun insert(uri: Uri, values: ContentValues?) = unsupported()
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) =
unsupported()
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?,
) = unsupported()
private fun unsupported(errorMessage: String? = null): Nothing =
throw UnsupportedOperationException(errorMessage)
}
@@ -1,7 +1,5 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.Signature import android.content.pm.Signature
@@ -27,7 +25,7 @@ import kotlinx.coroutines.launch
import java.security.MessageDigest import java.security.MessageDigest
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity(), Request.RequestListener {
private var ready = 0 private var ready = 0
private var playerFragment = PlayerFragment() private var playerFragment = PlayerFragment()
@@ -41,29 +39,27 @@ class MainActivity : FragmentActivity() {
private lateinit var gestureDetector: GestureDetector private lateinit var gestureDetector: GestureDetector
private val handler = Handler() private val handler = Handler()
private val delayHideMain: Long = 5000 private val delayHideMain: Long = 10000
private val delayHideSetting: Long = 10000 private val delayHideSetting: Long = 10000
lateinit var sharedPref: SharedPreferences // init {
private var channelReversal = false // lifecycleScope.launch(Dispatchers.IO) {
private var channelNum = true // val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
private var bootStartup = true //
// utilsJob.start()
init { //
lifecycleScope.launch(Dispatchers.IO) { // utilsJob.await()
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() } // }
// }
utilsJob.start()
utilsJob.await()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate") Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
Request.onCreate()
Request.setRequestListener(this)
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 window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_HIDE_NAVIGATION
@@ -78,12 +74,6 @@ class MainActivity : FragmentActivity() {
.commit() .commit()
} }
gestureDetector = GestureDetector(this, GestureListener()) gestureDetector = GestureDetector(this, GestureListener())
sharedPref = getPreferences(Context.MODE_PRIVATE)
channelReversal = sharedPref.getBoolean(CHANNEL_REVERSAL, channelReversal)
channelNum = sharedPref.getBoolean(CHANNEL_NUM, channelNum)
bootStartup = sharedPref.getBoolean(BOOT_STARTUP, bootStartup)
val packageInfo = getPackageInfo() val packageInfo = getPackageInfo()
val versionName = packageInfo.versionName val versionName = packageInfo.versionName
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -91,13 +81,18 @@ class MainActivity : FragmentActivity() {
} else { } else {
packageInfo.versionCode.toLong() packageInfo.versionCode.toLong()
} }
settingFragment = settingFragment = SettingFragment(
SettingFragment(versionName, versionCode, channelReversal, channelNum, bootStartup) versionName,
versionCode,
SP.channelReversal,
SP.channelNum,
SP.bootStartup
)
} }
fun showInfoFragment(tvViewModel: TVViewModel) { fun showInfoFragment(tvViewModel: TVViewModel) {
infoFragment.show(tvViewModel) infoFragment.show(tvViewModel)
if (channelNum) { if (SP.channelNum) {
channelFragment.show(tvViewModel) channelFragment.show(tvViewModel)
} }
} }
@@ -111,7 +106,7 @@ class MainActivity : FragmentActivity() {
return return
} }
if (channelNum) { if (SP.channelNum) {
channelFragment.show(channel) channelFragment.show(channel)
} }
} }
@@ -185,7 +180,7 @@ class MainActivity : FragmentActivity() {
fun fragmentReady() { fun fragmentReady() {
ready++ ready++
Log.i(TAG, "ready $ready") Log.i(TAG, "ready $ready")
if (ready == 4) { if (ready == 5) {
mainFragment.fragmentReady() mainFragment.fragmentReady()
} }
} }
@@ -238,30 +233,6 @@ class MainActivity : FragmentActivity() {
} }
} }
fun saveChannelReversal(channelReversal: Boolean) {
with(sharedPref.edit()) {
putBoolean(CHANNEL_REVERSAL, channelReversal)
apply()
}
this.channelReversal = channelReversal
}
fun saveChannelNum(channelNum: Boolean) {
with(sharedPref.edit()) {
putBoolean(CHANNEL_NUM, channelNum)
apply()
}
this.channelNum = channelNum
}
fun saveBootStartup(bootStartup: Boolean) {
with(sharedPref.edit()) {
putBoolean(BOOT_STARTUP, bootStartup)
apply()
}
this.bootStartup = bootStartup
}
private fun showSetting() { private fun showSetting() {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
return return
@@ -285,7 +256,7 @@ class MainActivity : FragmentActivity() {
private fun channelUp() { private fun channelUp() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
if (channelReversal) { if (SP.channelReversal) {
next() next()
return return
} }
@@ -302,7 +273,7 @@ class MainActivity : FragmentActivity() {
private fun channelDown() { private fun channelDown() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
if (channelReversal) { if (SP.channelReversal) {
prev() prev()
return return
} }
@@ -545,10 +516,17 @@ class MainActivity : FragmentActivity() {
handler.removeCallbacks(hideMain) handler.removeCallbacks(hideMain)
} }
companion object { override fun onDestroy() {
private const val TAG = "MainActivity" super.onDestroy()
private const val CHANNEL_REVERSAL = "channel_reversal" Request.onDestroy()
private const val CHANNEL_NUM = "channel_num"
const val BOOT_STARTUP = "boot_startup"
} }
override fun onRequestFinished() {
fragmentReady()
}
private companion object {
const val TAG = "MainActivity"
}
} }
@@ -1,6 +1,5 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@@ -19,6 +18,7 @@ import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter import androidx.leanback.widget.RowPresenter
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.Utils.getDateTimestamp import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.models.TVListViewModel import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -30,12 +30,8 @@ class MainFragment : BrowseSupportFragment() {
private var rowsAdapter: ArrayObjectAdapter? = null private var rowsAdapter: ArrayObjectAdapter? = null
private var request = Request()
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private lateinit var sharedPref: SharedPreferences
private var lastVideoUrl = "" private var lastVideoUrl = ""
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@@ -55,8 +51,7 @@ class MainFragment : BrowseSupportFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
activity?.let { request.initYSP(it) } activity?.let { YSP.init(it) }
sharedPref = (activity as? MainActivity)?.sharedPref!!
loadRows() loadRows()
@@ -91,7 +86,7 @@ class MainFragment : BrowseSupportFragment() {
if (tvViewModel.pid.value != "") { if (tvViewModel.pid.value != "") {
Log.i(TAG, "request $title") Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { request.fetchData(it) } tvViewModel.let { Request.fetchData(it) }
} }
(activity as? MainActivity)?.showInfoFragment(tvViewModel) (activity as? MainActivity)?.showInfoFragment(tvViewModel)
setSelectedPosition( setSelectedPosition(
@@ -155,7 +150,7 @@ class MainFragment : BrowseSupportFragment() {
adapter = rowsAdapter adapter = rowsAdapter
itemPosition = sharedPref.getInt(POSITION, 0) itemPosition = SP.itemPosition
if (itemPosition >= tvListViewModel.size()) { if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0 itemPosition = 0
} }
@@ -287,11 +282,11 @@ class MainFragment : BrowseSupportFragment() {
if (timestamp - tvViewModel.programUpdateTime > 60) { if (timestamp - tvViewModel.programUpdateTime > 60) {
if (tvViewModel.program.value!!.isEmpty()) { if (tvViewModel.program.value!!.isEmpty()) {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) Request.fetchProgram(tvViewModel)
} else { } else {
if (tvViewModel.program.value!!.last().et - timestamp < 600) { if (tvViewModel.program.value!!.last().et - timestamp < 600) {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) Request.fetchProgram(tvViewModel)
} }
} }
} }
@@ -317,11 +312,8 @@ class MainFragment : BrowseSupportFragment() {
override fun onStop() { override fun onStop() {
Log.i(TAG, "onStop") Log.i(TAG, "onStop")
super.onStop() super.onStop()
with(sharedPref.edit()) { SP.itemPosition = itemPosition
putInt(POSITION, itemPosition) Log.i(TAG, "position saved")
apply()
}
Log.i(TAG, "$POSITION $itemPosition saved")
} }
override fun onDestroy() { override fun onDestroy() {
@@ -334,6 +326,5 @@ class MainFragment : BrowseSupportFragment() {
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
private const val POSITION = "position"
} }
} }
+140 -33
View File
@@ -1,6 +1,5 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Base64 import android.util.Base64
@@ -12,8 +11,10 @@ import com.lizongying.mytv.api.AuthRequest
import com.lizongying.mytv.api.FAuth import com.lizongying.mytv.api.FAuth
import com.lizongying.mytv.api.FAuthService import com.lizongying.mytv.api.FAuthService
import com.lizongying.mytv.api.Info import com.lizongying.mytv.api.Info
import com.lizongying.mytv.api.InfoV2
import com.lizongying.mytv.api.LiveInfo import com.lizongying.mytv.api.LiveInfo
import com.lizongying.mytv.api.LiveInfoRequest import com.lizongying.mytv.api.LiveInfoRequest
import com.lizongying.mytv.api.Token
import com.lizongying.mytv.api.YSP import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.api.YSPApiService import com.lizongying.mytv.api.YSPApiService
import com.lizongying.mytv.api.YSPBtraceService import com.lizongying.mytv.api.YSPBtraceService
@@ -28,20 +29,22 @@ import retrofit2.Response
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import kotlin.math.max
class Request { object Request {
private const val TAG = "Request"
private var yspTokenService: YSPTokenService = ApiClient().yspTokenService private var yspTokenService: YSPTokenService = ApiClient().yspTokenService
private var yspApiService: YSPApiService = ApiClient().yspApiService private var yspApiService: YSPApiService = ApiClient().yspApiService
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
private var fAuthService: FAuthService = ApiClient().fAuthService private var fAuthService: FAuthService = ApiClient().fAuthService
private var ysp: YSP? = null
private var token = "" private var token = ""
private var tokenFH = ""
private var needAuth = false private var needAuth = false
// TODO onDestroy
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private lateinit var btraceRunnable: BtraceRunnable private lateinit var btraceRunnable: BtraceRunnable
private var tokenRunnable: TokenRunnable = TokenRunnable() private var tokenRunnable: TokenRunnable = TokenRunnable()
@@ -50,12 +53,16 @@ class Request {
private val input = private val input =
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,ANGLEMetalRenderer:AppleM1Pro,UnspecifiedVersion)"}""".toByteArray() """{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,ANGLEMetalRenderer:AppleM1Pro,UnspecifiedVersion)"}""".toByteArray()
init { private var listener: RequestListener? = null
handler.post(tokenRunnable)
fun onCreate() {
Log.i(TAG, "onCreate")
fetchInfoV2()
} }
fun initYSP(context: Context) { fun onDestroy() {
ysp = YSP(context) Log.i(TAG, "onDestroy")
handler.removeCallbacks(tokenRunnable)
} }
var call: Call<LiveInfo>? = null var call: Call<LiveInfo>? = null
@@ -66,9 +73,9 @@ class Request {
val title = tvModel.title.value val title = tvModel.title.value
val data = ysp?.getAuthData(tvModel) val data = YSP.getAuthData(tvModel)
val request = data?.let { AuthRequest(it) } val request = AuthRequest(data)
callAuth = request?.let { yspApiService.getAuth("guid=${ysp?.getGuid()}; $cookie", it) } callAuth = request.let { yspApiService.getAuth("guid=${YSP.getGuid()}; $cookie", it) }
callAuth?.enqueue(object : Callback<Auth> { callAuth?.enqueue(object : Callback<Auth> {
override fun onResponse(call: Call<Auth>, response: Response<Auth>) { override fun onResponse(call: Call<Auth>, response: Response<Auth>) {
@@ -77,7 +84,7 @@ class Request {
if (liveInfo?.data?.token != null) { if (liveInfo?.data?.token != null) {
Log.i(TAG, "token ${liveInfo.data.token}") Log.i(TAG, "token ${liveInfo.data.token}")
ysp?.token = liveInfo.data.token YSP.token = liveInfo.data.token
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else { } else {
Log.e(TAG, "$title token error") Log.e(TAG, "$title token error")
@@ -147,12 +154,12 @@ class Request {
val title = tvModel.title.value val title = tvModel.title.value
tvModel.seq = 0 tvModel.seq = 0
val data = ysp?.switch(tvModel) val data = YSP.switch(tvModel)
val request = data?.let { LiveInfoRequest(it) } val request = LiveInfoRequest(data)
call = request?.let { call = request.let {
yspApiService.getLiveInfo( yspApiService.getLiveInfo(
"guid=${ysp?.getGuid()}; $cookie", "guid=${YSP.getGuid()}; $cookie",
ysp!!.token, YSP.token,
it it
) )
} }
@@ -323,6 +330,7 @@ class Request {
} }
fun fetchVideo(tvModel: TVViewModel) { fun fetchVideo(tvModel: TVViewModel) {
Log.d(TAG, "fetchVideo")
if (token == "") { if (token == "") {
yspTokenService.getInfo("") yspTokenService.getInfo("")
.enqueue(object : Callback<Info> { .enqueue(object : Callback<Info> {
@@ -377,7 +385,12 @@ class Request {
val title = tvModel.title.value val title = tvModel.title.value
fAuth = fAuthService.getAuth(tvModel.getTV().pid, "HD") var qa = "HD"
if (tokenFH != "") {
qa = "FHD"
}
fAuth = fAuthService.getAuth(tokenFH, tvModel.getTV().pid, qa)
fAuth?.enqueue(object : Callback<FAuth> { fAuth?.enqueue(object : Callback<FAuth> {
override fun onResponse(call: Call<FAuth>, response: Response<FAuth>) { override fun onResponse(call: Call<FAuth>, response: Response<FAuth>) {
if (response.isSuccessful && response.body()?.data?.live_url != null) { if (response.isSuccessful && response.body()?.data?.live_url != null) {
@@ -406,6 +419,7 @@ class Request {
} }
fun fetchData(tvModel: TVViewModel) { fun fetchData(tvModel: TVViewModel) {
Log.d(TAG, "fetchData")
if (tvModel.getTV().channel == "港澳台") { if (tvModel.getTV().channel == "港澳台") {
fetchFAuth(tvModel) fetchFAuth(tvModel)
return return
@@ -428,32 +442,121 @@ class Request {
} }
} }
inner class TokenRunnable : Runnable { class TokenRunnable : Runnable {
override fun run() { override fun run() {
fetchToken() fetchToken()
handler.postDelayed(this, 600000)
} }
} }
fun fetchToken() { private fun fetchInfoV2() {
yspTokenService.getInfo(token) yspTokenService.getInfoV2()
.enqueue(object : Callback<Info> { .enqueue(object : Callback<InfoV2> {
override fun onResponse(call: Call<Info>, response: Response<Info>) { override fun onResponse(call: Call<InfoV2>, response: Response<InfoV2>) {
if (response.isSuccessful) { if (response.isSuccessful) {
token = response.body()?.data?.token!! val t = response.body()?.t
Log.i(TAG, "info success $token") val f = response.body()?.f
val e = response.body()?.e
val c = response.body()?.c
if (!t.isNullOrEmpty()) {
token = t
Log.i(TAG, "token success $token")
if (e != null) {
handler.postDelayed(
tokenRunnable,
max(600000, (e - 1500) * 1000).toLong()
)
} else {
Log.e(TAG, "e empty")
handler.postDelayed(
tokenRunnable,
600000
)
}
} else {
Log.e(TAG, "token empty")
handler.postDelayed(
tokenRunnable,
600000
)
}
if (!f.isNullOrEmpty()) {
tokenFH = f
Log.i(TAG, "tokenFH success $tokenFH")
}
if (c != null) {
Utils.setBetween(c * 1000L)
Log.i(TAG, "current time $c")
}
} else { } else {
Log.e(TAG, "token status error") Log.e(TAG, "token status error")
handler.postDelayed(
tokenRunnable,
600000
)
} }
listener?.onRequestFinished()
} }
override fun onFailure(call: Call<Info>, t: Throwable) { override fun onFailure(call: Call<InfoV2>, t: Throwable) {
Log.e(TAG, "token request error $t") Log.e(TAG, "token request error $t")
handler.postDelayed(
tokenRunnable,
600000
)
listener?.onRequestFinished()
} }
}) })
} }
inner class BtraceRunnable(private val tvModel: TVViewModel) : Runnable { fun fetchToken() {
yspTokenService.getToken(token)
.enqueue(object : Callback<Token> {
override fun onResponse(call: Call<Token>, response: Response<Token>) {
if (response.isSuccessful) {
val t = response.body()?.t
val e = response.body()?.e
if (!t.isNullOrEmpty()) {
token = t
Log.e(TAG, "token success $token")
if (e != null) {
handler.postDelayed(
tokenRunnable,
max(600000, (e - 1500) * 1000).toLong()
)
} else {
Log.e(TAG, "e empty")
handler.postDelayed(
tokenRunnable,
600000
)
}
} else {
Log.e(TAG, "token empty")
handler.postDelayed(
tokenRunnable,
600000
)
}
} else {
Log.e(TAG, "token status error")
handler.postDelayed(
tokenRunnable,
600000
)
}
}
override fun onFailure(call: Call<Token>, t: Throwable) {
Log.e(TAG, "token request error $t")
handler.postDelayed(
tokenRunnable,
600000
)
}
})
}
class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() { override fun run() {
fetchBtrace(tvModel) fetchBtrace(tvModel)
handler.postDelayed(this, 60000) handler.postDelayed(this, 60000)
@@ -463,11 +566,11 @@ class Request {
fun fetchBtrace(tvModel: TVViewModel) { fun fetchBtrace(tvModel: TVViewModel) {
val title = tvModel.title.value val title = tvModel.title.value
val guid = ysp?.getGuid()!! val guid = YSP.getGuid()
val pid = tvModel.pid.value!! val pid = tvModel.pid.value!!
val sid = tvModel.sid.value!! val sid = tvModel.sid.value!!
yspBtraceService.kvcollect( yspBtraceService.kvcollect(
c_timestamp = ysp?.generateGuid()!!, c_timestamp = YSP.generateGuid(),
guid = guid, guid = guid,
c_guid = guid, c_guid = guid,
prog = sid, prog = sid,
@@ -475,7 +578,7 @@ class Request {
fpid = pid, fpid = pid,
livepid = pid, livepid = pid,
sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid", sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid",
playno = ysp?.getRand()!!, playno = YSP.getRand(),
ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"), ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"),
seq = tvModel.seq.toString(), seq = tvModel.seq.toString(),
) )
@@ -567,7 +670,11 @@ class Request {
} }
} }
companion object { interface RequestListener {
private const val TAG = "Request" fun onRequestFinished()
}
fun setRequestListener(listener: RequestListener) {
this.listener = listener
} }
} }
@@ -0,0 +1,48 @@
package com.lizongying.mytv
import android.content.Context
import android.content.SharedPreferences
object SP {
// Name of the sp file TODO Should use a meaningful name and do migrations
private const val SP_FILE_NAME = "MainActivity"
// If Change channel with up and down in reversed order or not
private const val KEY_CHANNEL_REVERSAL = "channel_reversal"
// If use channel num to select channel or not
private const val KEY_CHANNEL_NUM = "channel_num"
// If start app on device boot or not
private const val KEY_BOOT_STARTUP = "boot_startup"
// Position in list of the selected channel item
private const val KEY_POSITION = "position"
// guid
private const val KEY_GUID = "guid"
private lateinit var sp: SharedPreferences
/**
* The method must be invoked as early as possible(At least before using the keys)
*/
fun init(context: Context) {
sp = context.getSharedPreferences(SP_FILE_NAME, Context.MODE_PRIVATE)
}
var channelReversal: Boolean
get() = sp.getBoolean(KEY_CHANNEL_REVERSAL, false)
set(value) = sp.edit().putBoolean(KEY_CHANNEL_REVERSAL, value).apply()
var channelNum: Boolean
get() = sp.getBoolean(KEY_CHANNEL_NUM, true)
set(value) = sp.edit().putBoolean(KEY_CHANNEL_NUM, value).apply()
var bootStartup: Boolean
get() = sp.getBoolean(KEY_BOOT_STARTUP, false)
set(value) = sp.edit().putBoolean(KEY_BOOT_STARTUP, value).apply()
var itemPosition: Int
get() = sp.getInt(KEY_POSITION, 0)
set(value) = sp.edit().putInt(KEY_POSITION, value).apply()
var guid: String
get() = sp.getString(KEY_GUID, "") ?: ""
set(value) = sp.edit().putString(KEY_GUID, value).apply()
}
@@ -39,21 +39,21 @@ class SettingFragment(
val switchChannelReversal = _binding?.switchChannelReversal val switchChannelReversal = _binding?.switchChannelReversal
switchChannelReversal?.isChecked = channelReversal switchChannelReversal?.isChecked = channelReversal
switchChannelReversal?.setOnCheckedChangeListener { _, isChecked -> switchChannelReversal?.setOnCheckedChangeListener { _, isChecked ->
(activity as MainActivity).saveChannelReversal(isChecked) SP.channelReversal = isChecked
(activity as MainActivity).settingActive() (activity as MainActivity).settingActive()
} }
val switchChannelNum = _binding?.switchChannelNum val switchChannelNum = _binding?.switchChannelNum
switchChannelNum?.isChecked = channelNum switchChannelNum?.isChecked = channelNum
switchChannelNum?.setOnCheckedChangeListener { _, isChecked -> switchChannelNum?.setOnCheckedChangeListener { _, isChecked ->
(activity as MainActivity).saveChannelNum(isChecked) SP.channelNum = isChecked
(activity as MainActivity).settingActive() (activity as MainActivity).settingActive()
} }
val switchBootStartup = _binding?.switchBootStartup val switchBootStartup = _binding?.switchBootStartup
switchBootStartup?.isChecked = bootStartup switchBootStartup?.isChecked = bootStartup
switchBootStartup?.setOnCheckedChangeListener { _, isChecked -> switchBootStartup?.setOnCheckedChangeListener { _, isChecked ->
(activity as MainActivity).saveBootStartup(isChecked) SP.bootStartup = isChecked
(activity as MainActivity).settingActive() (activity as MainActivity).settingActive()
} }
@@ -873,10 +873,24 @@ object TVList {
true, true,
mustToken = false mustToken = false
), ),
TV(
57,
"CETV1",
"CETV1",
listOf(),
0,
"地方",
"",
"600171827",
"2022823801",
"",
true,
mustToken = false
),
), ),
"港澳台" to listOf( "港澳台" to listOf(
TV( TV(
57, 58,
"凤凰卫视资讯台", "凤凰卫视资讯台",
"", "",
listOf(), listOf(),
@@ -890,7 +904,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
58, 59,
"凤凰卫视中文台", "凤凰卫视中文台",
"", "",
listOf(), listOf(),
@@ -904,7 +918,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
59, 60,
"凤凰卫视香港台", "凤凰卫视香港台",
"", "",
listOf(), listOf(),
@@ -920,7 +934,7 @@ object TVList {
), ),
"国际" to listOf( "国际" to listOf(
TV( TV(
60, 61,
"CGTN", "CGTN",
"CGTN", "CGTN",
listOf("http://live.cgtn.com/1000/prog_index.m3u8"), listOf("http://live.cgtn.com/1000/prog_index.m3u8"),
@@ -934,7 +948,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
61, 62,
"CGTN 法语频道", "CGTN 法语频道",
"CGTN法语频道", "CGTN法语频道",
listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"), listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"),
@@ -948,7 +962,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
62, 63,
"CGTN 俄语频道", "CGTN 俄语频道",
"CGTN俄语频道", "CGTN俄语频道",
listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"), listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"),
@@ -962,7 +976,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
63, 64,
"CGTN 阿拉伯语频道", "CGTN 阿拉伯语频道",
"CGTN阿拉伯语频道", "CGTN阿拉伯语频道",
listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"), listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"),
@@ -976,7 +990,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
64, 65,
"CGTN 西班牙语频道", "CGTN 西班牙语频道",
"CGTN西班牙语频道", "CGTN西班牙语频道",
listOf( listOf(
@@ -993,7 +1007,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
65, 66,
"CGTN 纪录频道", "CGTN 纪录频道",
"CGTN外语纪录频道", "CGTN外语纪录频道",
listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"), listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"),
@@ -26,6 +26,10 @@ object Utils {
return (System.currentTimeMillis() - between) / 1000 return (System.currentTimeMillis() - between) / 1000
} }
fun setBetween(currentTimeMillis: Long) {
between = System.currentTimeMillis() - currentTimeMillis
}
suspend fun init() { suspend fun init() {
var currentTimeMillis: Long = 0 var currentTimeMillis: Long = 0
try { try {
@@ -2,11 +2,13 @@ package com.lizongying.mytv.api
import retrofit2.Call import retrofit2.Call
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query import retrofit2.http.Query
interface FAuthService { interface FAuthService {
@GET("api/v3/hub/live/auth-url") @GET("api/v3/hub/live/auth-url")
fun getAuth( fun getAuth(
@Header("token") token: String,
@Query("live_id") live_id: String = "", @Query("live_id") live_id: String = "",
@Query("live_qa") live_qa: String = "", @Query("live_qa") live_qa: String = "",
): Call<FAuth> ): Call<FAuth>
@@ -1,6 +1,5 @@
package com.lizongying.mytv.api package com.lizongying.mytv.api
data class Info( data class Info(
val code: Int?, val code: Int?,
val msg: String?, val msg: String?,
@@ -11,6 +10,18 @@ data class Info(
) )
} }
data class Token(
val e: Int?,
val t: String?,
)
data class InfoV2(
val f: String?,
val t: String?,
val e: Int?,
val c: Int?,
)
data class Release( data class Release(
val code: Int?, val code: Int?,
val msg: String?, val msg: String?,
@@ -1,15 +1,17 @@
package com.lizongying.mytv.api package com.lizongying.mytv.api
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import com.lizongying.mytv.Encryptor import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.SP
import com.lizongying.mytv.Utils.getDateTimestamp import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import kotlin.math.floor import kotlin.math.floor
import kotlin.random.Random import kotlin.random.Random
class YSP(var context: Context) { object YSP {
private const val TAG = "YSP"
private var cnlid = "" private var cnlid = ""
private var livepid = "" private var livepid = ""
@@ -53,17 +55,10 @@ class YSP(var context: Context) {
private var appid = "ysp_pc" private var appid = "ysp_pc"
var token = "" var token = ""
private var encryptor: Encryptor? = null private var encryptor = Encryptor()
private lateinit var sharedPref: SharedPreferences
init {
if (context is MainActivity) {
encryptor = Encryptor()
encryptor!!.init(context)
sharedPref = (context as MainActivity).sharedPref
}
fun init(context: Context) {
encryptor.init(context)
guid = getGuid() guid = getGuid()
} }
@@ -81,7 +76,7 @@ class YSP(var context: Context) {
timeStr = getTimeStr() timeStr = getTimeStr()
cKey = cKey =
encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform) encryptor.encrypt(cnlid, timeStr, appVer, guid, platform)
signature = getSignature() 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":"$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"}"""
} }
@@ -110,23 +105,17 @@ class YSP(var context: Context) {
} }
fun getGuid(): String { fun getGuid(): String {
var guid = sharedPref.getString("guid", "") var guid = SP.guid
if (guid == null || guid.length < 18) { if (guid.length < 18) {
guid = generateGuid() guid = generateGuid()
with(sharedPref.edit()) { SP.guid = guid
putString("guid", guid)
apply()
}
} }
return guid return guid
} }
private fun newGuid(): String { private fun newGuid(): String {
guid = generateGuid() guid = generateGuid()
with(sharedPref.edit()) { SP.guid = guid
putString("guid", guid)
apply()
}
return guid return guid
} }
@@ -143,18 +132,14 @@ class YSP(var context: Context) {
private fun getSignature(): String { private fun getSignature(): String {
val e = val e =
"adjust=${adjust}&appVer=${appVer}&app_version=$appVersion&cKey=$cKey&channel=$channel&cmd=$cmd&cnlid=$cnlid&defn=${defn}&devid=${devid}&dtype=${dtype}&encryptVer=${encryptVer}&guid=${guid}&livepid=${livepid}&otype=${otype}&platform=${platform}&rand_str=${randStr}&sphttps=${sphttps}&stream=${stream}".toByteArray() "adjust=${adjust}&appVer=${appVer}&app_version=$appVersion&cKey=$cKey&channel=$channel&cmd=$cmd&cnlid=$cnlid&defn=${defn}&devid=${devid}&dtype=${dtype}&encryptVer=${encryptVer}&guid=${guid}&livepid=${livepid}&otype=${otype}&platform=${platform}&rand_str=${randStr}&sphttps=${sphttps}&stream=${stream}".toByteArray()
val hashedData = encryptor?.hash(e) ?: return "" val hashedData = encryptor.hash(e) ?: return ""
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
} }
private fun getAuthSignature(): String { private fun getAuthSignature(): String {
val e = val e =
"appid=${appid}&guid=${guid}&pid=${livepid}&rand_str=${randStr}".toByteArray() "appid=${appid}&guid=${guid}&pid=${livepid}&rand_str=${randStr}".toByteArray()
val hashedData = encryptor?.hash2(e) ?: return "" val hashedData = encryptor.hash2(e) ?: return ""
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
} }
companion object {
private const val TAG = "YSP"
}
} }
@@ -10,4 +10,13 @@ interface YSPTokenService {
fun getInfo( fun getInfo(
@Query("token") token: String = "", @Query("token") token: String = "",
): Call<Info> ): Call<Info>
@GET("my-tv/v2/token")
fun getToken(
@Query("token") token: String = "",
): Call<Token>
@GET("my-tv/v2/info")
fun getInfoV2(
): Call<InfoV2>
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+6 -6
View File
@@ -863,7 +863,7 @@
{ {
"id": 56, "id": 56,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
"pid": "600014550", "pid": "600014550",
"sid": "2001656803", "sid": "2001656803",
@@ -878,7 +878,7 @@
{ {
"id": 57, "id": 57,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
"pid": "600084704", "pid": "600084704",
"sid": "2010153503", "sid": "2010153503",
@@ -893,7 +893,7 @@
{ {
"id": 58, "id": 58,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
"pid": "600084758", "pid": "600084758",
"sid": "2010152603", "sid": "2010152603",
@@ -908,7 +908,7 @@
{ {
"id": 59, "id": 59,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
"pid": "600084782", "pid": "600084782",
"sid": "2010155203", "sid": "2010155203",
@@ -923,7 +923,7 @@
{ {
"id": 60, "id": 60,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
"pid": "600084744", "pid": "600084744",
"sid": "2010152503", "sid": "2010152503",
@@ -939,7 +939,7 @@
{ {
"id": 61, "id": 61,
"videoIndex": 0, "videoIndex": 0,
"channel": "国际频道", "channel": "国际",
"logo": "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp", "logo": "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
"pid": "600084781", "pid": "600084781",
"sid": "2010155403", "sid": "2010155403",