Skip to content

Commit

Permalink
🎨 完善应用设置
Browse files Browse the repository at this point in the history
  • Loading branch information
yaoxieyoulei committed Apr 18, 2024
1 parent 30793d8 commit 3d0a361
Show file tree
Hide file tree
Showing 12 changed files with 649 additions and 238 deletions.
13 changes: 13 additions & 0 deletions .idea/appInsightsSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 1 addition & 14 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
</div>
<p>使用Android原生开发的电视直播软件</p>

<img src="https://github.com/yaoxieyoulei/my_tv/blob/main/screenshots/shot_3.png?raw=true" width="96%"/>
<br/>
<img src="https://github.com/yaoxieyoulei/my_tv/blob/main/screenshots/shot_1.png?raw=true" width="96%"/>
<br/>
<img src="https://github.com/yaoxieyoulei/my_tv/blob/main/screenshots/shot_2.png?raw=true" width="96%"/>
<img src="https://github.com/yaoxieyoulei/my_tv/blob/main/screenshots/shot_3.png?raw=true" width="48%"/>
<img src="https://github.com/yaoxieyoulei/my_tv/blob/main/screenshots/shot_2.png?raw=true" width="48%"/>
</div>

## 使用
Expand All @@ -26,13 +25,11 @@
- 频道选择:OK键;单击屏幕;
- 设置页面:菜单、帮助键、长按OK键;双击屏幕;

### 自定义直播源

1. 进入设置页面
2. 请求网址:`http://<设备IP>:10481`
3. 按界面提示操作
### 自定义设置

不支持多源,只会选择频道的第一个源,其他忽略
- 访问以下网址:`http://<设备IP>:10481`
- 支持自定义直播源、节目单等
- 直播源不支持多源,只会选择频道的第一个源,其他忽略

## 下载

Expand All @@ -42,7 +39,7 @@

- 主要解决 [my_tv](https://github.com/yaoxieyoulei/my_tv)(flutter)在低端设备上播放(4k)视频卡顿掉帧
- 仅支持Android5及以上
- 网络环境必须支持IPV6
- 网络环境必须支持IPV6(默认直播源)
- 只在自家电视上测过,其他电视稳定性未知

## 功能
Expand All @@ -51,8 +48,9 @@
- [x] 数字选台
- [x] 节目单
- [x] 开机自启
- [ ] 自动更新
- [ ] 自定义直播源
- [x] 自动更新
- [x] 自定义直播源
- [x] 自定义节目单
- [ ] 性能优化

## 更新日志
Expand Down
11 changes: 5 additions & 6 deletions app/src/main/java/top/yogiczy/mytv/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import top.yogiczy.mytv.ui.App
import top.yogiczy.mytv.ui.theme.MyTVTheme
import top.yogiczy.mytv.ui.utils.HttpServer
import top.yogiczy.mytv.ui.utils.SP
import kotlin.system.exitProcess


@AndroidEntryPoint
Expand Down Expand Up @@ -56,16 +57,14 @@ class MainActivity : ComponentActivity() {
LocalContentColor provides MaterialTheme.colorScheme.onSurface
) {
App(
onBackPressed = { finishAffinity() }
onBackPressed = {
finish()
exitProcess(0)
}
)
}
}
}
}
}

override fun onDestroy() {
HttpServer.stop()
super.onDestroy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import top.yogiczy.mytv.data.entities.Epg
import top.yogiczy.mytv.data.entities.EpgList
import top.yogiczy.mytv.data.entities.EpgProgramme
import top.yogiczy.mytv.data.entities.EpgProgrammeList
import top.yogiczy.mytv.data.utils.Constants
import top.yogiczy.mytv.ui.utils.SP
import java.io.File
import java.io.StringReader
Expand All @@ -33,7 +32,7 @@ class EpgRepositoryImpl(private val context: Context) : EpgRepository {
val xml = getXml()
val hashCode = filteredChannels.hashCode()

if (SP.epgCacheHash == hashCode) {
if (SP.epgCachedHash == hashCode) {
val cache = getCache()
if (cache != null) {
Log.d(TAG, "使用缓存epg")
Expand All @@ -43,16 +42,16 @@ class EpgRepositoryImpl(private val context: Context) : EpgRepository {

val epgList = parseFromXml(xml, filteredChannels)
setCache(epgList)
SP.epgCacheHash = hashCode
SP.epgCachedHash = hashCode

return epgList
}

private suspend fun fetchXml(): String = withContext(Dispatchers.IO) {
Log.d(TAG, "获取远程xml: ${Constants.EPG_XML_URL}")
Log.d(TAG, "获取远程xml: ${SP.epgXmlUrl}")

val client = OkHttpClient()
val request = Request.Builder().url(Constants.EPG_XML_URL).build()
val request = Request.Builder().url(SP.epgXmlUrl).build()

try {
return@withContext with(client.newCall(request).execute()) {
Expand Down Expand Up @@ -84,23 +83,25 @@ class EpgRepositoryImpl(private val context: Context) : EpgRepository {
private suspend fun getXml(): String {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())

if (dateFormat.format(System.currentTimeMillis()) == dateFormat.format(SP.epgXmlCacheTime)) {
if (dateFormat.format(System.currentTimeMillis()) == dateFormat.format(SP.epgXmlCachedAt)) {
val cache = getCacheXml()
if (cache.isNotBlank()) {
Log.d(TAG, "使用缓存xml")
return cache
}
} else {
if (Calendar.getInstance().get(Calendar.HOUR_OF_DAY) < 1) {
Log.d(TAG, "未到1点,不刷新epg")
if (Calendar.getInstance()
.get(Calendar.HOUR_OF_DAY) < SP.epgRefreshTimeThreshold
) {
Log.d(TAG, "未到时间点,不刷新epg")
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
}
}

val xml = fetchXml()
setCacheXml(xml)
SP.epgXmlCacheTime = System.currentTimeMillis()
SP.epgCacheHash = 0
SP.epgXmlCachedAt = System.currentTimeMillis()
SP.epgCachedHash = 0

return xml
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import top.yogiczy.mytv.data.entities.IptvGroup
import top.yogiczy.mytv.data.entities.IptvGroupList
import top.yogiczy.mytv.data.entities.IptvList
import top.yogiczy.mytv.data.models.IptvResponseItem
import top.yogiczy.mytv.data.utils.Constants
import top.yogiczy.mytv.ui.utils.SP
import java.io.File
import javax.inject.Singleton
Expand All @@ -21,7 +20,7 @@ class IptvRepositoryImpl(private val context: Context) : IptvRepository {
override suspend fun getIptvGroups(): IptvGroupList {
val now = System.currentTimeMillis()

if (now - SP.iptvSourceCacheTime < 24 * 60 * 60 * 1000) {
if (now - SP.iptvSourceCachedAt < SP.iptvSourceCacheTime) {
val cache = getCache()
if (cache.isNotBlank()) {
Log.d(TAG, "使用缓存直播源")
Expand All @@ -31,22 +30,20 @@ class IptvRepositoryImpl(private val context: Context) : IptvRepository {

val data = fetchSource()
setCache(data)
SP.iptvSourceCacheTime = now
SP.iptvSourceCachedAt = now

return parseSource(data)
}

private fun getSource() = SP.iptvCustomSource.ifBlank { Constants.IPTV_SOURCE_URL }

private fun getSourceType(): SourceType {
return if (getSource().endsWith(".m3u")) SourceType.M3U else SourceType.TVBOX
return if (SP.iptvSourceUrl.endsWith(".m3u")) SourceType.M3U else SourceType.TVBOX
}

private suspend fun fetchSource(): String = withContext(Dispatchers.IO) {
Log.d(TAG, "获取远程直播源: ${getSource()}")
Log.d(TAG, "获取远程直播源: ${SP.iptvSourceUrl}")

val client = OkHttpClient()
val request = Request.Builder().url(getSource()).build()
val request = Request.Builder().url(SP.iptvSourceUrl).build()

try {
return@withContext with(client.newCall(request).execute()) {
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/top/yogiczy/mytv/data/utils/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ object Constants {
const val IPTV_SOURCE_URL =
"https://mirror.ghproxy.com/https://raw.githubusercontent.com/zhumeng11/IPTV/main/IPTV.m3u"

/**
* IPTV源缓存时间(毫秒)
*/
const val IPTV_SOURCE_CACHE_TIME = 1000 * 60 * 60 * 24L // 24小时

/**
* 节目单XML地址
*/
const val EPG_XML_URL = "https://live.fanmingming.com/e.xml"

/**
* 节目单刷新时间阈值(小时)
*/
const val EPG_REFRESH_TIME_THRESHOLD = 6 // 不到6点不刷新

/**
* GitHub最新版本信息
*/
Expand All @@ -25,4 +35,14 @@ object Constants {
* GitHub加速代理地址
*/
const val GITHUB_PROXY = "https://mirror.ghproxy.com/"

/**
* HTTP请求重试次数
*/
const val HTTP_RETRY_COUNT = 10L

/**
* HTTP请求重试间隔时间(毫秒)
*/
const val HTTP_RETRY_INTERVAL = 3000L
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import top.yogiczy.mytv.data.entities.EpgProgrammeList
import top.yogiczy.mytv.data.entities.IptvGroupList
import top.yogiczy.mytv.data.repositories.EpgRepository
import top.yogiczy.mytv.data.repositories.IptvRepository
import top.yogiczy.mytv.ui.utils.SP
import javax.inject.Inject

@HiltViewModel
Expand All @@ -31,47 +32,38 @@ class HomeScreeViewModel @Inject constructor(

init {
viewModelScope.launch {
flow { emit(iptvRepository.getIptvGroups()) }
.retryWhen { _, attempt ->
if (attempt >= 10) return@retryWhen false
flow { emit(iptvRepository.getIptvGroups()) }.retryWhen { _, attempt ->
if (attempt >= SP.httpRetryCount) return@retryWhen false

uiState.value =
HomeScreenUiState.Loading("获取远程直播源(${attempt + 1}/10)...")
delay(3000)
true
}
.catch { uiState.value = HomeScreenUiState.Error(it.message) }
.map {
uiState.value = HomeScreenUiState.Ready(iptvGroupList = it)
it
}
uiState.value =
HomeScreenUiState.Loading("获取远程直播源(${attempt + 1}/${SP.httpRetryCount})...")
delay(SP.httpRetryInterval)
true
}.catch { uiState.value = HomeScreenUiState.Error(it.message) }.map {
uiState.value = HomeScreenUiState.Ready(iptvGroupList = it)
it
}
// 开始获取epg
.flatMapLatest { iptvGroupList ->
val channels =
iptvGroupList.flatMap { it.iptvs }.map { iptv -> iptv.channelName }
flow { emit(epgRepository.getEpgs(channels)) }
}
.retry(10) { delay(3000); true }
.catch { emit(EpgList()) }
.map { epgList ->
}.retry(SP.httpRetryCount) { delay(SP.httpRetryInterval); true }
.catch { emit(EpgList()) }.map { epgList ->
// 移除过期节目
epgList.copy(
value = epgList.map { epg ->
epg.copy(
programmes = EpgProgrammeList(
epg.programmes.filter { programme ->
System.currentTimeMillis() < programme.endAt
},
)
epgList.copy(value = epgList.map { epg ->
epg.copy(
programmes = EpgProgrammeList(
epg.programmes.filter { programme ->
System.currentTimeMillis() < programme.endAt
},
)
}
)
}
.map { epgList ->
uiState.value = (uiState.value as HomeScreenUiState.Ready)
.copy(epgList = epgList)
}
.collect()
)
})
}.map { epgList ->
uiState.value =
(uiState.value as HomeScreenUiState.Ready).copy(epgList = epgList)
}.collect()
}
}
}
Expand Down
Loading

0 comments on commit 3d0a361

Please sign in to comment.