├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── libs
│ ├── lib-decoder-ffmpeg-release.aar
│ └── lib-decoder-vp9-release.aar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── ww
│ │ └── simpletv
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── ww
│ │ │ └── simpletv
│ │ │ ├── AppUtils.kt
│ │ │ ├── BaseActivity.kt
│ │ │ ├── ChannelUtils.kt
│ │ │ ├── Constant.kt
│ │ │ ├── DownloadStatus.kt
│ │ │ ├── FontSizeActivity.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── PlayerActivity.kt
│ │ │ ├── RetryInterceptor.kt
│ │ │ ├── TVApplication.kt
│ │ │ ├── TVBootReceive.kt
│ │ │ ├── adapter
│ │ │ ├── ChannelAdapter.kt
│ │ │ ├── FontSizeAdapter.kt
│ │ │ └── GroupAdapter.kt
│ │ │ ├── bean
│ │ │ ├── TV.kt
│ │ │ └── VersionInfo.kt
│ │ │ └── dialog
│ │ │ ├── BaseDialogFragment.kt
│ │ │ ├── ChannelListDialog.kt
│ │ │ ├── SettingDialog.kt
│ │ │ └── UpdateDialog.kt
│ └── res
│ │ ├── drawable
│ │ ├── arrow.xml
│ │ ├── btn_selector.xml
│ │ ├── focus_bg.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_logo.xml
│ │ ├── item_focus_selector.xml
│ │ ├── place_icon.xml
│ │ ├── simple_tv_icon.xml
│ │ ├── switch_selector.xml
│ │ ├── switch_thumb_selector.xml
│ │ └── switch_track_selector.xml
│ │ ├── layout
│ │ ├── activity_font_size.xml
│ │ ├── activity_main.xml
│ │ ├── activity_player.xml
│ │ ├── dialog_channel.xml
│ │ ├── dialog_setting.xml
│ │ ├── dialog_update.xml
│ │ ├── item_channel.xml
│ │ ├── item_font_size.xml
│ │ └── item_group.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── bg_window.png
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── bg_window.png
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── bg_window.png
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── bg_window.png
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── bg_window.png
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── bg_window.png
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── colors.xml
│ │ ├── dimen.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ ├── data_extraction_rules.xml
│ │ └── provider.xml
│ └── test
│ └── java
│ └── com
│ └── ww
│ └── simpletv
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── a1.png
├── control.jpg
├── q1.png
├── q2.png
└── telegram.png
├── m3u
└── ipv6
│ └── IPTV.m3u
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 | replay_pid*
25 | *.iml
26 | .gradle
27 | /local.properties
28 | .idea
29 | .DS_Store
30 | /build
31 | /captures
32 | .externalNativeBuild
33 | .cxx
34 | local.properties
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | **1.出现“初始化频道列表失败,请尝试手动更新或退出重试”、“播放失败,网络连接超时,请稍后重试”**
4 |
5 | 
6 | 
7 |
8 | 1.请尝试手动更新频道源,更新成功之后退出重新进入,如果更新失败请确认网络是否连通
9 | 
10 |
11 | 2.如果更新成功后还是无法播放,可能该频道源已失效,请切换频道观看,耐心等待更新修复
12 | 3.请确认网络是否能访问IPV6,测试地址测试[地址1](http://test-ipv6.com/),[地址2](http://ipv6-test.ch/index.html.zh_CN)。如果已经路由器开启IPV6但是无法访问IPV6,请重启路由器,建议开启路由器每日定时重启功能,防止IPV6失效。
13 |
14 | # SimpleTV
15 |
16 | 1.观看电视直播,支持央视和地方台。 操作简单,打开即看,方便中老年人使用,让电视回归本质。
17 | 2.部分频道目前仅支持IPV6网络,IPV4网络无法播放。
18 | 3.IPV6测试[地址1](http://test-ipv6.com/),[地址2](http://ipv6-test.ch/index.html.zh_CN),如显示运营商已接入IPV6但无法访问IPV6网站,需要在路由器设置打开IPV6功能([华为路由器参考](https://consumer.huawei.com/cn/support/content/zh-cn00685838/#:~:text=%E9%80%9A%E8%BF%87%E6%99%BA%E6%85%A7%E7%94%9F%E6%B4%BB%20App%20%E8%AE%BE%E7%BD%AE%201%20%E6%89%8B%E6%9C%BA%2F%E5%B9%B3%E6%9D%BF%E8%BF%9E%E6%8E%A5%E5%88%B0%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%20Wi-Fi%E3%80%82%202%20%E6%89%93%E5%BC%80%E6%99%BA%E6%85%A7%E7%94%9F%E6%B4%BB,%E7%82%B9%E5%87%BB%20IPv6%20%E3%80%82%20%E7%82%B9%E5%87%BB%20IPv6%20%E5%BC%80%E5%85%B3%EF%BC%8C%E5%BC%80%E5%90%AF%E6%88%96%E5%85%B3%E9%97%AD%20IPv6%20%E5%8A%9F%E8%83%BD%E3%80%82),[小米路由器参考](https://cdn.cnbj1.fds.api.mi-img.com/ics-resources/articles/6055c933ec317cb4ee2d0103.html))
19 | 4.仅支持安卓6.0及以上版本
20 | 5.所有素材及直播源均来源于互联网,**仅供测试研究,不得商用**。如有侵权,请联系我删除。
21 |
22 | # 安装
23 |
24 | 1.下载apk后使用U盘安装(TCL电视系统屏蔽了apk文件,需要用自带的电视卫视进入U盘安装)
25 | 2.使用当贝市场远程推送安装([参考](https://zhuanlan.zhihu.com/p/588748827#:~:text=%E6%95%99%E7%A8%8B%E4%BB%8B%E7%BB%8D%20%E6%AD%A5%E9%AA%A4%E4%B8%80%EF%BC%9A%20%E9%A6%96%E5%85%88%E6%89%93%E5%BC%80%E5%BD%93%E8%B4%9D%E5%B8%82%E5%9C%BA%E7%9A%84%E7%AE%A1%E7%90%86%E7%95%8C%E9%9D%A2%EF%BC%8C%20%E5%A6%82%E5%9B%BE%E6%89%80%E7%A4%BA%20%E7%9A%84%E4%BD%8D%E7%BD%AE%E5%8F%AF%E4%BB%A5%E7%9C%8B%E5%88%B0%20%E2%80%9C%E8%BF%9C%E7%A8%8B%E6%8E%A8%E9%80%81%E2%80%9D%E7%9A%84%E5%9B%BE%E6%A0%87%20%E6%AD%A5%E9%AA%A4%E4%BA%8C%EF%BC%9A,%E6%9C%89%20%E4%B8%A4%E7%A7%8D%E8%BF%9E%E6%8E%A5%E6%96%B9%E5%BC%8F%20%EF%BC%8C%E8%BF%9C%E7%A8%8B%E6%8E%A8%E9%80%81%E5%BC%80%E5%A7%8B%E5%89%8D%E9%9C%80%E8%A6%81%E5%9C%A8%20%E6%89%8B%E6%9C%BA%E4%B8%8A%E6%8F%90%E5%89%8D%E4%B8%8B%E8%BD%BD%E5%A5%BD%E9%9C%80%E8%A6%81%E6%8E%A8%E9%80%81%E7%9A%84%E8%BD%AF%E4%BB%B6%20%E6%AD%A5%E9%AA%A4%E4%B8%89%EF%BC%9A%20%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E4%B8%80%E6%89%8B%E6%9C%BA%E6%89%AB%E7%A0%81%E5%90%8E%E4%BC%9A%20%E8%87%AA%E5%8A%A8%E8%B7%B3%E8%BD%AC%E5%88%B0%E8%BF%9C%E7%A8%8B%E6%8E%A8%E9%80%81%E7%9A%84%E9%A1%B5%E9%9D%A2%20%EF%BC%8C%E7%82%B9%E5%87%BB%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6%EF%BC%8C%E5%B0%86%E4%B8%8B%E8%BD%BD%E5%A5%BD%E7%9A%84%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0))
26 | 3.打开电视开发者模式,连接电脑使用adb安装
27 |
28 | # 操作说明
29 |
30 | 小米遥控器示例
31 | 
32 |
33 | # 频道展示
34 |
35 | 小米盒子4C
36 | 
37 | 
38 | 
39 | 
40 | 
41 |
42 | # 致谢
43 |
44 | 直播来源:
45 | https://github.com/Meroser/IPTV
46 | https://github.com/fanmingming/live
47 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | .gradle
3 | /release
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | id("kotlin-kapt")
5 | }
6 |
7 | android {
8 | namespace = "com.ww.simpletv"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | applicationId = "com.ww.simpletv"
13 | minSdk = 23
14 | targetSdk = 34
15 | versionCode = 4
16 | versionName = "1.0.3"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | isMinifyEnabled = false
24 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility = JavaVersion.VERSION_1_8
29 | targetCompatibility = JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = "1.8"
33 | }
34 | buildFeatures {
35 | dataBinding = true
36 | }
37 | }
38 |
39 | dependencies {
40 |
41 | implementation("androidx.core:core-ktx:1.12.0")
42 | implementation("androidx.appcompat:appcompat:1.6.1")
43 | implementation("androidx.leanback:leanback:1.0.0")
44 | implementation("com.google.android.material:material:1.11.0")
45 | implementation("androidx.constraintlayout:constraintlayout:2.1.4")
46 | implementation("com.github.bumptech.glide:glide:4.16.0")
47 | implementation("com.tencent:mmkv:1.3.3")
48 | implementation("com.google.code.gson:gson:2.10")
49 | testImplementation("junit:junit:4.13.2")
50 | androidTestImplementation("androidx.test.ext:junit:1.1.5")
51 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
52 | implementation("androidx.media3:media3-exoplayer:1.2.1")
53 | implementation("androidx.media3:media3-exoplayer-hls:1.2.1")
54 | implementation("androidx.media3:media3-ui:1.2.1")
55 | implementation("com.squareup.okhttp3:okhttp:4.12.0")
56 | implementation(files("libs/lib-decoder-ffmpeg-release.aar"))
57 | implementation(files("libs/lib-decoder-vp9-release.aar"))
58 | }
59 |
--------------------------------------------------------------------------------
/app/libs/lib-decoder-ffmpeg-release.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/libs/lib-decoder-ffmpeg-release.aar
--------------------------------------------------------------------------------
/app/libs/lib-decoder-vp9-release.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/libs/lib-decoder-vp9-release.aar
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -dontwarn com.tencent.bugly.**
23 | -keep public class com.tencent.bugly.**{*;}
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ww/simpletv/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.ww.simpletv", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
16 |
17 |
29 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
58 |
59 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
73 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/AppUtils.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 | import android.os.Build
8 | import android.util.Log
9 | import androidx.core.content.FileProvider
10 | import com.google.gson.Gson
11 | import com.ww.simpletv.bean.VersionInfo
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.delay
14 | import kotlinx.coroutines.flow.Flow
15 | import kotlinx.coroutines.flow.catch
16 | import kotlinx.coroutines.flow.flow
17 | import kotlinx.coroutines.flow.flowOn
18 | import kotlinx.coroutines.flow.onCompletion
19 | import okhttp3.OkHttpClient
20 | import okhttp3.Request
21 | import java.io.File
22 | import java.io.FileInputStream
23 | import java.io.FileOutputStream
24 | import java.io.InputStream
25 | import java.security.MessageDigest
26 | import java.security.NoSuchAlgorithmException
27 |
28 | /**
29 | *
30 | * Copyright (C), 2024 Potato-66, All rights reserved.
31 | * 创建时间: 2024/3/18
32 | * @since 1.0
33 | * @version 1.0
34 | * @author Potato-66
35 | */
36 | object AppUtils {
37 | private const val TAG = "AppUtils"
38 |
39 | fun getAppVersionName(context: Context): String {
40 | return try {
41 | context.packageManager.getPackageInfo(context.packageName, 0).versionName
42 | } catch (e: Exception) {
43 | Log.e(TAG, "getAppVersion: error:${e.message}")
44 | ""
45 | }
46 | }
47 |
48 | fun getAppVersionCode(context: Context): Long {
49 | return try {
50 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
51 | context.packageManager.getPackageInfo(context.packageName, 0).longVersionCode
52 | } else {
53 | context.packageManager.getPackageInfo(context.packageName, 0).versionCode.toLong()
54 | }
55 | } catch (e: Exception) {
56 | Log.e(TAG, "getAppVersionCode: error:${e.message}")
57 | 0
58 | }
59 | }
60 |
61 | fun getVersion(): VersionInfo? {
62 | try {
63 | val build = OkHttpClient.Builder().build()
64 | build.newCall(Request.Builder().url(API.URL_VERSION).build()).execute().use {
65 | if (it.isSuccessful) {
66 | it.body?.string()?.let { json ->
67 | return Gson().fromJson(json, VersionInfo::class.java) as VersionInfo
68 | }
69 | }
70 | }
71 | } catch (e: Exception) {
72 | Log.e(TAG, "checkVersion error:${e.message}")
73 | }
74 | return null
75 | }
76 |
77 | fun download(dir: File, versionInfo: VersionInfo?): Flow {
78 | var inputStream: InputStream? = null
79 | var outputStream: FileOutputStream? = null
80 | return flow {
81 | val client = OkHttpClient.Builder().build()
82 | val url = versionInfo?.url ?: ""
83 | client.newCall(Request.Builder().url(url).build()).execute().use { response ->
84 | if (response.isSuccessful) {
85 | response.body?.let { body ->
86 | val totalLength = body.contentLength()
87 | inputStream = body.byteStream()
88 | val file = File(dir, Constant.APK_NAME)
89 | if (file.exists()) {
90 | file.delete()
91 | }
92 | outputStream = FileOutputStream(file)
93 | val buffer = ByteArray(8 * 1024)
94 | var len: Int
95 | var curLength = 0
96 | var progress = 0
97 | while ((inputStream!!.read(buffer).also { len = it } != -1)) {
98 | outputStream!!.write(buffer, 0, len)
99 | curLength += len
100 | val curProgress = (curLength * 100 / totalLength).toInt()
101 | if (curProgress - progress >= 1) {
102 | progress = curProgress
103 | emit(DownloadStatus.DownLoading(progress))
104 | }
105 | }
106 | outputStream!!.flush()
107 | }
108 | } else {
109 | Log.e(TAG, "请求失败,error code:${response.code}")
110 | emit(DownloadStatus.Fail(response.code))
111 | }
112 | }
113 | }.onCompletion {
114 | Log.e(TAG, "download: onCompletion")
115 | inputStream?.close()
116 | outputStream?.close()
117 | delay(1000)
118 | val file = File(dir, Constant.APK_NAME)
119 | if (file.exists()) {
120 | val md5 = getMD5(file)
121 | if (md5 != versionInfo?.md5) {
122 | Log.e(TAG, "download: md5:$md5,remote md5:${versionInfo?.md5}")
123 | emit(DownloadStatus.Fail(-1))
124 | } else {
125 | emit(DownloadStatus.Success)
126 | }
127 | }
128 | }.catch { error ->
129 | Log.e(TAG, "download: catch error:${error.message}")
130 | inputStream?.close()
131 | outputStream?.close()
132 | emit(DownloadStatus.Error(error))
133 | }.flowOn(Dispatchers.IO)
134 | }
135 |
136 | fun installApk(context: Context) {
137 | val intent = Intent(Intent.ACTION_VIEW)
138 | val file = File(context.externalCacheDir, Constant.APK_NAME)
139 | intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
140 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
141 | intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
142 | intent.setDataAndType(
143 | FileProvider.getUriForFile(context, "com.ww.simpletv.provider", file),
144 | "application/vnd.android.package-archive"
145 | )
146 | } else {
147 | intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
148 | }
149 | context.startActivity(intent)
150 | }
151 |
152 | @OptIn(ExperimentalStdlibApi::class)
153 | fun getMD5(file: File): String {
154 | var inputStream: FileInputStream? = null
155 | return try {
156 | MessageDigest.getInstance("MD5").run {
157 | reset()
158 | inputStream = FileInputStream(file)
159 | val buffer = ByteArray(8 * 1024)
160 | var len: Int
161 | while ((inputStream!!.read(buffer).also { len = it }) != -1) {
162 | update(buffer, 0, len)
163 | }
164 | digest().toHexString()
165 | }
166 | } catch (e: NoSuchAlgorithmException) {
167 | Log.e(TAG, "getMD5: error:${e.message}")
168 | ""
169 | } catch (e: IllegalArgumentException) {
170 | Log.e(TAG, "getMD5: error:${e.message}")
171 | ""
172 | } finally {
173 | inputStream?.close()
174 | }
175 | }
176 |
177 | fun setFontScale(context: Context?, fontScale: Float) {
178 | val resources = context?.resources
179 | resources?.let {
180 | val configuration = it.configuration
181 | configuration.fontScale = fontScale
182 | context.createConfigurationContext(configuration)
183 | }
184 | }
185 |
186 | fun recreateActivity(activity: Activity) {
187 | activity.recreate()
188 | }
189 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.Context
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.tencent.mmkv.MMKV
6 |
7 | open class BaseActivity : AppCompatActivity() {
8 | override fun attachBaseContext(newBase: Context?) {
9 | AppUtils.setFontScale(newBase, MMKV.defaultMMKV().decodeFloat(Constant.KEY_FONT_SIZE, Constant.FONT_SIZE_NORMAL))
10 | super.attachBaseContext(newBase)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/ChannelUtils.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import com.tencent.mmkv.MMKV
6 | import com.ww.simpletv.bean.TV
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import okhttp3.OkHttpClient
10 | import okhttp3.Request
11 | import java.io.File
12 |
13 | /**
14 | *
15 | * Copyright (C), 2024 Potato-66, All rights reserved.
16 | * 创建时间: 2024/2/26
17 | * @since 1.0
18 | * @version 1.0
19 | * @author Potato-66
20 | */
21 | object ChannelUtils {
22 | private const val TAG = "ChannelUtils"
23 | val channelSet = linkedSetOf()
24 |
25 | suspend fun updateChannel(context: Context): Boolean {
26 | channelSet.clear()
27 | parseChannel(context)
28 | return channelSet.isNotEmpty()
29 | }
30 |
31 | suspend fun parseChannel(context: Context) {
32 | return withContext(Dispatchers.IO) {
33 | val file = getChannelFile(context)
34 | if (file == null) {
35 | Log.e(TAG, "parseChannel: iptv file not exist")
36 | return@withContext
37 | }
38 | val lines = file.readLines()
39 | if (!lines[0].startsWith("#EXTM3U")) {
40 | Log.e(TAG, "parseChannel: Non standard m3u8 file, parsing error")
41 | return@withContext
42 | }
43 | var id = ""
44 | var name = ""
45 | var group = ""
46 | var logo = ""
47 | lines.forEach { line ->
48 | if (line.startsWith("#EXTINF")) {
49 | line.split(" ")
50 | Regex("tvg-id=\"([^\"]+)\"").find(line)?.groups?.get(1)?.let {
51 | id = it.value
52 | }
53 | Regex("tvg-logo=\"(.*?)\"").find(line)?.groups?.get(1)?.let {
54 | logo = it.value
55 | }
56 | Regex("group-title=\"([^\"]*)\"").find(line)?.groups?.get(1)?.let {
57 | group = it.value
58 | }
59 | name = line.substring(line.indexOf(",") + 1)
60 | } else if (line.startsWith("http")) {
61 | channelSet.add(TV(id, name, group, logo, url = line))
62 | }
63 | }
64 | }
65 | }
66 |
67 | private fun getChannelFile(context: Context): File? {
68 | val file = File(context.filesDir, Constant.FILE_NAME)
69 | return if (!file.exists()) {
70 | if (downloadIPTVFile(file)) file else null
71 | } else {
72 | if (MMKV.defaultMMKV().decodeBool(Constant.KEY_AUTO_UPDATE, true)) {
73 | val lastModified = file.lastModified()
74 | if (System.currentTimeMillis() - lastModified > 24 * 60 * 60 * 1000) {
75 | try {
76 | if (downloadIPTVFile(file)) {
77 | Log.e(TAG, "getChannelFile update iptv file success")
78 | } else {
79 | Log.e(TAG, "getChannelFile update iptv file fail")
80 | }
81 | } catch (e: Exception) {
82 | e.printStackTrace()
83 | Log.e(TAG, "auto update iptv file fail")
84 | }
85 | }
86 | }
87 | file
88 | }
89 | }
90 |
91 | fun downloadIPTVFile(file: File): Boolean {
92 | val client =
93 | OkHttpClient.Builder().followRedirects(false).addInterceptor(RetryInterceptor()).build()
94 | val url = API.URL_M3U
95 | return client.newCall(Request.Builder().url(url).build()).execute().use {
96 | if (it.isSuccessful) {
97 | file.createNewFile()
98 | it.body?.let { body ->
99 | file.writeText(body.string())
100 | true
101 | } ?: false
102 | } else {
103 | Log.e(TAG, "download iptv file fail${it.message}}")
104 | false
105 | }
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/Constant.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | /**
4 | *
5 | * Copyright (C), 2024 Potato-66, All rights reserved.
6 | * 创建时间: 2024/2/26
7 | * @since 1.0
8 | * @version 1.0
9 | * @author Potato-66
10 | */
11 | object Constant {
12 | const val FILE_NAME = "IPTV.m3u"
13 | const val KEY_LAST_CHANNEL = "last_channel"
14 | const val KEY_BOOT_STARTUP = "boot_startup"
15 | const val KEY_AUTO_UPDATE = "auto_update"
16 | const val KEY_INIT = "init"
17 | const val KEY_FONT_SIZE = "font_size"
18 | const val DIALOG_TAG_SETTING = "setting"
19 | const val DIALOG_TAG_CHANNEL = "channel"
20 | const val DIALOG_TAG_UPDATE = "update"
21 | const val APK_NAME = "SimpleTV.apk"
22 | const val FONT_SIZE_NORMAL = 1.0f
23 | const val FONT_SIZE_LARGE = 1.2f
24 | const val FONT_SIZE_HUGE = 1.4f
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/DownloadStatus.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | /**
4 | *
5 | * Copyright (C), 2024 Potato-66, All rights reserved.
6 | * 创建时间: 2024/3/20
7 | * @since 1.0
8 | * @version 1.0
9 | * @author Potato-66
10 | */
11 | sealed class DownloadStatus {
12 | data class DownLoading(val progress: Int) : DownloadStatus()
13 | data class Fail(val code: Int) : DownloadStatus()
14 | data class Error(val error: Throwable) : DownloadStatus()
15 | data object Success : DownloadStatus()
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/FontSizeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.view.Window
6 | import android.view.WindowManager
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.databinding.DataBindingUtil
9 | import com.tencent.mmkv.MMKV
10 | import com.ww.simpletv.adapter.FontSizeAdapter
11 | import com.ww.simpletv.databinding.ActivityFontSizeBinding
12 |
13 | /**
14 | *
15 | * Copyright (C), 2024 Potato-66, All rights reserved.
16 | * 创建时间: 2024/3/19
17 | * @since 1.0
18 | * @version 1.0
19 | * @author Potato-66
20 | */
21 | class FontSizeActivity : AppCompatActivity() {
22 |
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | val lp = window.attributes
26 | lp.width = (resources.displayMetrics.widthPixels * 0.5f).toInt()
27 | lp.width = WindowManager.LayoutParams.WRAP_CONTENT
28 | lp.alpha = 0.9f
29 | window.attributes = lp
30 | supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
31 | val binding = DataBindingUtil.setContentView(
32 | this,
33 | R.layout.activity_font_size
34 | )
35 | val font = MMKV.defaultMMKV().decodeFloat(Constant.KEY_FONT_SIZE, Constant.FONT_SIZE_NORMAL)
36 | val index = when (font) {
37 | Constant.FONT_SIZE_NORMAL -> 0
38 | Constant.FONT_SIZE_LARGE -> 1
39 | else -> 2
40 | }
41 | val data = resources.getStringArray(R.array.list_font_size).asList()
42 | val adapter = FontSizeAdapter(this, data)
43 | binding.lvMenu.adapter = adapter
44 | binding.lvMenu.setItemChecked(index, true)
45 | binding.lvMenu.setOnItemClickListener { _, _, position, _ ->
46 | val fontScale = when (position) {
47 | 0 -> Constant.FONT_SIZE_NORMAL
48 | 1 -> Constant.FONT_SIZE_LARGE
49 | else -> Constant.FONT_SIZE_HUGE
50 | }
51 | if (font != fontScale) {
52 | MMKV.defaultMMKV().encode(Constant.KEY_FONT_SIZE, fontScale)
53 | AppUtils.setFontScale(this, fontScale)
54 | AppUtils.recreateActivity(this)
55 | }
56 | finish()
57 | }
58 | }
59 |
60 | override fun attachBaseContext(newBase: Context?) {
61 | AppUtils.setFontScale(newBase, 1.0f)
62 | super.attachBaseContext(newBase)
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.view.KeyEvent
7 | import android.view.View
8 | import android.view.WindowManager
9 | import android.widget.Toast
10 | import androidx.databinding.DataBindingUtil
11 | import androidx.lifecycle.lifecycleScope
12 | import com.tencent.mmkv.MMKV
13 | import com.ww.simpletv.databinding.ActivityMainBinding
14 | import com.ww.simpletv.dialog.SettingDialog
15 | import kotlinx.coroutines.CoroutineExceptionHandler
16 | import kotlinx.coroutines.launch
17 |
18 | /**
19 | *
20 | * Copyright (C), 2024 Potato-66, All rights reserved.
21 | * 创建时间: 2024/3/6
22 | * @since 1.0
23 | * @version 1.0
24 | * @author Potato-66
25 | */
26 | class MainActivity : BaseActivity() {
27 | companion object{
28 | private const val TAG = "MainActivity"
29 | }
30 |
31 | override fun onCreate(savedInstanceState: Bundle?) {
32 | super.onCreate(savedInstanceState)
33 | window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
34 | window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
35 | val binding =
36 | DataBindingUtil.setContentView(this, R.layout.activity_main) as ActivityMainBinding
37 | val isInit = MMKV.defaultMMKV().decodeBool(Constant.KEY_INIT, true)
38 | val handler = CoroutineExceptionHandler { _, throwable ->
39 | Log.e(TAG, "onCreate: exception:${throwable.message}")
40 | if (isInit) {
41 | binding.tvMessage.text = getString(R.string.init_channel_list_fail_network_error)
42 | } else {
43 | Toast.makeText(this, R.string.update_channel_list_fail_network_error, Toast.LENGTH_LONG).show()
44 | startActivity(Intent(this@MainActivity, PlayerActivity::class.java))
45 | finish()
46 | }
47 | binding.progress.hide()
48 | }
49 | lifecycleScope.launch(handler) {
50 | if (isInit) {
51 | binding.tvMessage.setText(R.string.init_channel_list)
52 | }
53 | ChannelUtils.parseChannel(this@MainActivity)
54 | binding.progress.hide()
55 | if (ChannelUtils.channelSet.isEmpty()) {
56 | Log.e(TAG, "onCreate: 没有频道列表,退出播放")
57 | binding.tvMessage.text = getString(R.string.load_channel_list_fail)
58 | } else {
59 | if (isInit) {
60 | MMKV.defaultMMKV().encode(Constant.KEY_INIT, false)
61 | }
62 | Log.e(TAG, "onCreate: channel size:${ChannelUtils.channelSet.size}")
63 | binding.tvMessage.visibility = View.GONE
64 | startActivity(Intent(this@MainActivity, PlayerActivity::class.java))
65 | finish()
66 | }
67 | }
68 | binding.main.setOnClickListener {
69 | SettingDialog().show(supportFragmentManager, Constant.DIALOG_TAG_SETTING)
70 | }
71 | }
72 |
73 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
74 | if (keyCode == KeyEvent.KEYCODE_MENU) {
75 | SettingDialog().show(supportFragmentManager, Constant.DIALOG_TAG_SETTING)
76 | return true
77 | } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B) {
78 | finish()
79 | }
80 | return super.onKeyDown(keyCode, event)
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/PlayerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import android.os.Looper
7 | import android.util.Log
8 | import android.util.Pair
9 | import android.view.KeyEvent
10 | import android.view.WindowManager
11 | import android.widget.Toast
12 | import androidx.annotation.OptIn
13 | import androidx.core.view.WindowCompat
14 | import androidx.core.view.WindowInsetsCompat
15 | import androidx.core.view.WindowInsetsControllerCompat
16 | import androidx.databinding.DataBindingUtil
17 | import androidx.media3.common.C
18 | import androidx.media3.common.ErrorMessageProvider
19 | import androidx.media3.common.MediaItem
20 | import androidx.media3.common.PlaybackException
21 | import androidx.media3.common.util.UnstableApi
22 | import androidx.media3.datasource.HttpDataSource.HttpDataSourceException
23 | import androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer
24 | import androidx.media3.decoder.vp9.LibvpxVideoRenderer
25 | import androidx.media3.exoplayer.DefaultRenderersFactory
26 | import androidx.media3.exoplayer.ExoPlayer
27 | import androidx.media3.exoplayer.Renderer
28 | import androidx.media3.exoplayer.audio.AudioRendererEventListener
29 | import androidx.media3.exoplayer.audio.AudioSink
30 | import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
31 | import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
32 | import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy
33 | import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy
34 | import androidx.media3.exoplayer.video.VideoRendererEventListener
35 | import com.google.gson.Gson
36 | import com.tencent.mmkv.MMKV
37 | import com.ww.simpletv.bean.TV
38 | import com.ww.simpletv.databinding.ActivityPlayerBinding
39 | import com.ww.simpletv.dialog.ChannelListDialog
40 | import com.ww.simpletv.dialog.SettingDialog
41 | import java.util.ArrayList
42 |
43 | /**
44 | *
45 | * Copyright (C), 2024 Potato-66, All rights reserved.
46 | * 创建时间: 2024/2/27
47 | * @since 1.0
48 | * @version 1.0
49 | * @author Potato-66
50 | */
51 | class PlayerActivity : BaseActivity() {
52 | private lateinit var binding: ActivityPlayerBinding
53 | private var tvList = mutableListOf()
54 | private var exoPlayer: ExoPlayer? = null
55 | private var dialog: ChannelListDialog? = null
56 | private var exitFlag = false
57 | private var lastTV: TV? = null
58 | private var curTVIndex = 0
59 | private var retryCount = 0
60 |
61 | companion object {
62 | private const val MAX_RETRY_COUNT = 3
63 | private const val TAG = "PlayerActivity"
64 | }
65 |
66 | @OptIn(UnstableApi::class)
67 | override fun onCreate(savedInstanceState: Bundle?) {
68 | super.onCreate(savedInstanceState)
69 | window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
70 | window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
71 | hideSystemUi()
72 | binding = DataBindingUtil.setContentView(this, R.layout.activity_player)
73 | tvList.addAll(ChannelUtils.channelSet.toMutableList())
74 | Log.e(TAG, "onCreate: tvList size ${tvList.size}")
75 | val mmkv = MMKV.defaultMMKV()
76 | val default = Gson().toJson(ChannelUtils.channelSet.first())
77 | val tvJson = mmkv.decodeString(Constant.KEY_LAST_CHANNEL, default)
78 | tvJson?.let { json ->
79 | val tv = Gson().fromJson(json, TV::class.java)
80 | tv?.let {
81 | lastTV = tv
82 | curTVIndex = tvList.indexOf(tv)
83 | val renderersFactory = object : DefaultRenderersFactory(this@PlayerActivity) {
84 | override fun buildAudioRenderers(
85 | context: Context,
86 | extensionRendererMode: Int,
87 | mediaCodecSelector: MediaCodecSelector,
88 | enableDecoderFallback: Boolean,
89 | audioSink: AudioSink,
90 | eventHandler: Handler,
91 | eventListener: AudioRendererEventListener,
92 | out: ArrayList
93 | ) {
94 | out.add(FfmpegAudioRenderer())
95 | super.buildAudioRenderers(
96 | context,
97 | extensionRendererMode,
98 | mediaCodecSelector,
99 | enableDecoderFallback,
100 | audioSink,
101 | eventHandler,
102 | eventListener,
103 | out
104 | )
105 | }
106 |
107 | override fun buildVideoRenderers(
108 | context: Context,
109 | extensionRendererMode: Int,
110 | mediaCodecSelector: MediaCodecSelector,
111 | enableDecoderFallback: Boolean,
112 | eventHandler: Handler,
113 | eventListener: VideoRendererEventListener,
114 | allowedVideoJoiningTimeMs: Long,
115 | out: ArrayList
116 | ) {
117 | out.add(LibvpxVideoRenderer(1000))
118 | super.buildVideoRenderers(
119 | context,
120 | extensionRendererMode,
121 | mediaCodecSelector,
122 | enableDecoderFallback,
123 | eventHandler,
124 | eventListener,
125 | allowedVideoJoiningTimeMs,
126 | out
127 | )
128 | }
129 | }.apply {
130 | setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
131 | }
132 | val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
133 | object : DefaultLoadErrorHandlingPolicy() {
134 | override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
135 | val errorCount = loadErrorInfo.errorCount
136 | val exception = loadErrorInfo.exception
137 | Log.e(TAG, "getRetryDelayMsFor:errorCount:$errorCount exception:${exception::class.java.simpleName}")
138 | return if (exception is HttpDataSourceException) {
139 | 5000
140 | } else {
141 | C.TIME_UNSET
142 | }
143 | }
144 | }
145 | exoPlayer = ExoPlayer.Builder(this@PlayerActivity, renderersFactory)
146 | .setMediaSourceFactory(
147 | DefaultMediaSourceFactory(this)
148 | .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
149 | )
150 | .build()
151 | exoPlayer?.run {
152 | playWhenReady = true
153 | setMediaItem(MediaItem.fromUri(tv.url))
154 | prepare()
155 | binding.exoPlay.player = this
156 | }
157 | // binding.exoPlay.setShutterBackgroundColor(Color.TRANSPARENT)
158 | }
159 | }
160 | binding.exoPlay.setOnClickListener {
161 | showChannelList()
162 | }
163 | binding.exoPlay.setOnLongClickListener {
164 | SettingDialog().show(supportFragmentManager, Constant.DIALOG_TAG_SETTING)
165 | true
166 | }
167 | binding.exoPlay.setErrorMessageProvider(object : ErrorMessageProvider {
168 | override fun getErrorMessage(throwable: PlaybackException): Pair {
169 | Log.e(TAG, "getErrorMessage: code:${throwable.errorCode} name:${throwable.errorCodeName} retryCount:$retryCount")
170 | when (throwable.errorCode) {
171 | PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
172 | PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT -> {
173 | return Pair.create(0, getString(R.string.play_network_error_hint))
174 | }
175 |
176 | PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED,
177 | PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
178 | PlaybackException.ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES -> {
179 | return Pair.create(0, getString(R.string.play_decode_error_hint))
180 | }
181 |
182 | PlaybackException.ERROR_CODE_DECODER_INIT_FAILED,
183 | PlaybackException.ERROR_CODE_DECODING_FAILED -> {
184 | return if (retryCount < MAX_RETRY_COUNT) {
185 | retryCount++
186 | exoPlayer?.prepare()
187 | Pair.create(0, "")
188 | } else {
189 | Pair.create(0, getString(R.string.play_decode_error_hint))
190 | }
191 | }
192 |
193 | PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
194 | exoPlayer?.prepare()
195 | return Pair.create(0, "")
196 | }
197 |
198 | else -> return if (retryCount < MAX_RETRY_COUNT) {
199 | retryCount++
200 | exoPlayer?.prepare()
201 | Pair.create(0, "")
202 | } else {
203 | Pair.create(0, getString(R.string.play_other_error_hint))
204 | }
205 | }
206 | }
207 | })
208 | }
209 |
210 | private fun hideSystemUi() {
211 | WindowCompat.setDecorFitsSystemWindows(window, false)
212 | WindowCompat.getInsetsController(window, window.decorView).run {
213 | hide(WindowInsetsCompat.Type.systemBars())
214 | systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
215 | }
216 | }
217 |
218 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
219 | Log.e(TAG, "onKeyDown: $keyCode")
220 | when (keyCode) {
221 | KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_BUTTON_SELECT,
222 | KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_NUMPAD_ENTER -> {
223 | showChannelList()
224 | return true
225 | }
226 |
227 | KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BUTTON_B -> {
228 | if (dialog?.isVisible == true) {
229 | dialog?.dismiss()
230 | } else if (exitFlag) {
231 | finish()
232 | } else {
233 | exitFlag = true
234 | Toast.makeText(this, R.string.exit_hint, Toast.LENGTH_SHORT).show()
235 | Handler(Looper.getMainLooper()).postDelayed({
236 | exitFlag = false
237 | }, 1500)
238 | }
239 | return true
240 | }
241 |
242 | KeyEvent.KEYCODE_DPAD_UP -> {
243 | if (curTVIndex == 0) {
244 | curTVIndex = tvList.size - 1
245 | } else {
246 | curTVIndex--
247 | }
248 | lastTV = tvList[curTVIndex]
249 | exoPlayer?.run {
250 | setMediaItem(MediaItem.fromUri(tvList[curTVIndex].url))
251 | prepare()
252 | }
253 | return true
254 | }
255 |
256 | KeyEvent.KEYCODE_DPAD_DOWN -> {
257 | if (curTVIndex == tvList.size - 1) {
258 | curTVIndex = 0
259 | } else {
260 | curTVIndex++
261 | }
262 | lastTV = tvList[curTVIndex]
263 | exoPlayer?.run {
264 | setMediaItem(MediaItem.fromUri(tvList[curTVIndex].url))
265 | prepare()
266 | }
267 | return true
268 | }
269 |
270 | KeyEvent.KEYCODE_MENU -> {
271 | SettingDialog().show(supportFragmentManager, Constant.DIALOG_TAG_SETTING)
272 | return true
273 | }
274 |
275 | }
276 | return super.onKeyDown(keyCode, event)
277 | }
278 |
279 | private fun showChannelList() {
280 | dialog = ChannelListDialog(ChannelUtils.channelSet, lastTV)
281 | dialog?.onChoose = { tv ->
282 | lastTV = tv
283 | curTVIndex = tvList.indexOf(tv)
284 | retryCount = 0
285 | exoPlayer?.run {
286 | setMediaItem(MediaItem.fromUri(tv.url))
287 | prepare()
288 | }
289 | }
290 | dialog?.show(supportFragmentManager, Constant.DIALOG_TAG_CHANNEL)
291 | }
292 |
293 | override fun onStart() {
294 | super.onStart()
295 | exoPlayer?.run {
296 | playWhenReady = true
297 | }
298 | }
299 |
300 | override fun onPause() {
301 | super.onPause()
302 | lastTV?.let {
303 | MMKV.defaultMMKV().encode(Constant.KEY_LAST_CHANNEL, Gson().toJson(lastTV))
304 | }
305 | }
306 |
307 | override fun onStop() {
308 | super.onStop()
309 | exoPlayer?.run {
310 | playWhenReady = false
311 | }
312 | }
313 |
314 | override fun onDestroy() {
315 | super.onDestroy()
316 | exoPlayer?.run {
317 | release()
318 | }
319 | }
320 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/RetryInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.util.Log
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | class RetryInterceptor : Interceptor {
8 | override fun intercept(chain: Interceptor.Chain): Response {
9 | Log.e("RetryInterceptor", "intercept:download github iptv file fail, retry")
10 | val request = chain.request()
11 | val url = API.URL_IPTV
12 | val newRequest = request.newBuilder().url(url).build()
13 | return chain.proceed(newRequest)
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/TVApplication.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.app.Application
4 | import com.tencent.mmkv.MMKV
5 |
6 | /**
7 | *
8 | * Copyright (C), 2024 Potato-66, All rights reserved.
9 | * 创建时间: 2024/2/27
10 | * @since 1.0
11 | * @version 1.0
12 | * @author Potato-66
13 | */
14 | class TVApplication : Application() {
15 | override fun onCreate() {
16 | super.onCreate()
17 | MMKV.initialize(this)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/TVBootReceive.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.util.Log
7 | import com.tencent.mmkv.MMKV
8 |
9 | /**
10 | *
11 | * Copyright (C), 2024 Potato-66, All rights reserved.
12 | * 创建时间: 2024/2/28
13 | * @since 1.0
14 | * @version 1.0
15 | * @author Potato-66
16 | */
17 | class TVBootReceive : BroadcastReceiver() {
18 | override fun onReceive(context: Context?, intent: Intent?) {
19 | Log.e("TVBootReceive", "onReceive: 开机")
20 | intent?.run {
21 | if (action == Intent.ACTION_BOOT_COMPLETED) {
22 | if (MMKV.defaultMMKV().decodeBool(Constant.KEY_BOOT_STARTUP, false)) {
23 | context?.run {
24 | startActivity(Intent(this, MainActivity::class.java).apply {
25 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
26 | })
27 | }
28 | }
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/adapter/ChannelAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.adapter
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.BaseAdapter
8 | import android.widget.ImageView
9 | import android.widget.TextView
10 | import com.bumptech.glide.Glide
11 | import com.bumptech.glide.request.RequestOptions
12 | import com.ww.simpletv.R
13 | import com.ww.simpletv.bean.TV
14 |
15 | /**
16 | *
17 | * Copyright (C), 2024 Potato-66, All rights reserved.
18 | * 创建时间: 2024/2/27
19 | * @since 1.0
20 | * @version 1.0
21 | * @author Potato-66
22 | */
23 | class ChannelAdapter(private val context: Context) : BaseAdapter() {
24 | private val tvs = arrayListOf()
25 |
26 | fun setChannelList(tvs: List) {
27 | if (tvs.isEmpty()) {
28 | return
29 | }
30 | this.tvs.clear()
31 | this.tvs.addAll(tvs)
32 | notifyDataSetChanged()
33 | }
34 |
35 | fun getChannelList(): List = tvs
36 |
37 | override fun getCount(): Int = tvs.size
38 |
39 | override fun getItem(p0: Int): TV = tvs[p0]
40 |
41 | override fun getItemId(p0: Int): Long = p0.toLong()
42 |
43 | override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
44 | val holder: GroupHolder
45 | var view: View? = p1
46 | if (view == null) {
47 | view = LayoutInflater.from(context).inflate(R.layout.item_channel, p2, false)
48 | holder = GroupHolder(view)
49 | } else {
50 | holder = view.tag as GroupHolder
51 | }
52 | holder.textView.text = tvs[p0].name
53 | holder.tvNumber.text = "${p0 +1}."
54 | Glide.with(context).load(tvs[p0].logo).apply(RequestOptions().placeholder(R.drawable.place_icon))
55 | .into(holder.icon)
56 | return view!!
57 | }
58 |
59 | inner class GroupHolder(view: View) {
60 | val icon: ImageView = view.findViewById(R.id.iv_icon)
61 | val textView: TextView = view.findViewById(R.id.tv_channel)
62 | val tvNumber: TextView = view.findViewById(R.id.tv_num)
63 |
64 | init {
65 | view.tag = this
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/adapter/FontSizeAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.adapter
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.BaseAdapter
9 | import android.widget.RadioButton
10 | import com.ww.simpletv.Constant
11 | import com.ww.simpletv.R
12 |
13 | /**
14 | *
15 | * Copyright (C), 2024 Potato-66, All rights reserved.
16 | * 创建时间: 2024/2/27
17 | * @since 1.0
18 | * @version 1.0
19 | * @author Potato-66
20 | */
21 | class FontSizeAdapter(private val context: Context, private val groups: List) : BaseAdapter() {
22 | override fun getCount(): Int = groups.size
23 |
24 | override fun getItem(p0: Int): String = groups[p0]
25 |
26 | override fun getItemId(p0: Int): Long = p0.toLong()
27 |
28 | override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
29 | val holder: GroupHolder
30 | var view: View? = p1
31 | if (view == null) {
32 | view = LayoutInflater.from(context).inflate(R.layout.item_font_size, p2, false)
33 | holder = GroupHolder(view)
34 | } else {
35 | holder = view.tag as GroupHolder
36 | }
37 | holder.radioButton.text = groups[p0]
38 | holder.radioButton.textSize = when (p0) {
39 | 0 -> 15 * Constant.FONT_SIZE_NORMAL
40 | 1 -> 15 * Constant.FONT_SIZE_LARGE
41 | else -> 15 * Constant.FONT_SIZE_HUGE
42 | }
43 | return view!!
44 | }
45 |
46 | inner class GroupHolder(view: View) {
47 | val radioButton: RadioButton = view.findViewById(R.id.rb_item)
48 |
49 | init {
50 | view.tag = this
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/adapter/GroupAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.adapter
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.BaseAdapter
8 | import android.widget.TextView
9 | import com.ww.simpletv.R
10 |
11 | /**
12 | *
13 | * Copyright (C), 2024 Potato-66, All rights reserved.
14 | * 创建时间: 2024/2/27
15 | * @since 1.0
16 | * @version 1.0
17 | * @author Potato-66
18 | */
19 | class GroupAdapter(private val context: Context, private val groups: List) : BaseAdapter() {
20 | override fun getCount(): Int = groups.size
21 |
22 | override fun getItem(p0: Int): String = groups[p0]
23 |
24 | override fun getItemId(p0: Int): Long = p0.toLong()
25 |
26 | override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
27 | val holder: GroupHolder
28 | var view: View? = p1
29 | if (view == null) {
30 | view = LayoutInflater.from(context).inflate(R.layout.item_group, p2, false)
31 | holder = GroupHolder(view)
32 | } else {
33 | holder = view.tag as GroupHolder
34 | }
35 | holder.textView.text = groups[p0]
36 | return view!!
37 | }
38 |
39 | inner class GroupHolder(view: View) {
40 | val textView: TextView = view.findViewById(R.id.tv_group)
41 |
42 | init {
43 | view.tag = this
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/bean/TV.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.bean
2 |
3 | import java.io.Serializable
4 |
5 | /**
6 | *
7 | * Copyright (C), 2024 Potato-66, All rights reserved.
8 | * 创建时间: 2024/2/27
9 | * @since 1.0
10 | * @version 1.0
11 | * @author Potato-66
12 | */
13 | data class TV(val id: String = "", val name: String = "", val group: String = "", val logo: String = "", var url: String = "") : Comparable,
14 | Serializable {
15 |
16 | override fun compareTo(other: TV): Int {
17 | return other.group.last() - this.group.last()
18 | }
19 |
20 | override fun hashCode(): Int {
21 | var result = id.hashCode()
22 | result = 31 * result + name.hashCode()
23 | result = 31 * result + group.hashCode()
24 | result = 31 * result + logo.hashCode()
25 | result = 31 * result + url.hashCode()
26 | return result
27 | }
28 |
29 | override fun equals(other: Any?): Boolean {
30 | if (this === other) return true
31 | if (javaClass != other?.javaClass) return false
32 |
33 | other as TV
34 |
35 | if (id != other.id) return false
36 | if (name != other.name) return false
37 | if (group != other.group) return false
38 | if (logo != other.logo) return false
39 | return url == other.url
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/bean/VersionInfo.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.bean
2 |
3 | /**
4 | *
5 | * Copyright (C), 2024 Potato-66, All rights reserved.
6 | * 创建时间: 2024/3/18
7 | * @since 1.0
8 | * @version 1.0
9 | * @author Potato-66
10 | */
11 | data class VersionInfo(
12 | val versionCode: Long,
13 | val versionName: String,
14 | val md5: String,
15 | val url: String,
16 | val iptv: String
17 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/dialog/BaseDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.dialog
2 |
3 | import android.os.Bundle
4 | import android.view.Gravity
5 | import android.view.KeyEvent
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import android.view.WindowManager
10 | import androidx.databinding.DataBindingUtil
11 | import androidx.databinding.ViewDataBinding
12 | import androidx.fragment.app.DialogFragment
13 | import com.ww.simpletv.Constant
14 |
15 | /**
16 | *
17 | * Copyright (C), 2024 Potato-66, All rights reserved.
18 | * 创建时间: 2024/2/27
19 | * @since 1.0
20 | * @version 1.0
21 | * @author Potato-66
22 | */
23 | abstract class BaseDialogFragment : DialogFragment() {
24 | lateinit var binding: T
25 |
26 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
27 | binding = DataBindingUtil.inflate(inflater, initLayoutResource(), container, false)
28 | initBindData()
29 | return binding.root
30 | }
31 |
32 | override fun onStart() {
33 | super.onStart()
34 | isCancelable = true
35 | dialog?.let {
36 | val window = it.window
37 | val params = window?.attributes
38 | params?.run {
39 | if (tag == Constant.DIALOG_TAG_UPDATE) {
40 | width = (resources.displayMetrics.widthPixels * 0.5).toInt()
41 | height = WindowManager.LayoutParams.WRAP_CONTENT
42 | gravity = Gravity.CENTER
43 | } else {
44 | width = (resources.displayMetrics.widthPixels * 0.4).toInt()
45 | height = resources.displayMetrics.heightPixels
46 | gravity = Gravity.START
47 | }
48 | dimAmount = 0f
49 | alpha = 0.9f
50 | window.attributes = params
51 | }
52 | it.setOnKeyListener { _, _, keyEvent ->
53 | if (keyEvent.keyCode == KeyEvent.KEYCODE_BACK && tag != Constant.DIALOG_TAG_UPDATE) {
54 | dismiss()
55 | true
56 | } else {
57 | false
58 | }
59 | }
60 | }
61 | }
62 |
63 | abstract fun initLayoutResource(): Int
64 |
65 | open fun initBindData() {}
66 |
67 | override fun onDestroy() {
68 | super.onDestroy()
69 | binding.unbind()
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/dialog/ChannelListDialog.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.dialog
2 |
3 | import com.ww.simpletv.R
4 | import com.ww.simpletv.bean.TV
5 | import com.ww.simpletv.adapter.ChannelAdapter
6 | import com.ww.simpletv.adapter.GroupAdapter
7 | import com.ww.simpletv.databinding.DialogChannelBinding
8 |
9 | /**
10 | *
11 | * Copyright (C), 2024 Potato-66, All rights reserved.
12 | * 创建时间: 2024/2/27
13 | * @since 1.0
14 | * @version 1.0
15 | * @author Potato-66
16 | */
17 | class ChannelListDialog(private val tvs: Set?, private val tv: TV?) : BaseDialogFragment() {
18 | var onChoose: ((tv: TV) -> Unit)? = null
19 |
20 | override fun initLayoutResource(): Int = R.layout.dialog_channel
21 |
22 | override fun onResume() {
23 | super.onResume()
24 | tvs?.let { tvs ->
25 | val map = tvs.groupBy { it.group.uppercase() }
26 | val groups = arrayListOf()
27 | groups.addAll(map.keys)
28 | binding.lvGroup.adapter = context?.let { GroupAdapter(it, groups) }
29 | var index = groups.indexOf(tv?.group?.uppercase())
30 | if (index == -1) {
31 | index = 0
32 | }
33 | binding.lvGroup.setSelection(index)
34 | val channelAdapter = context?.let { ChannelAdapter(it) }
35 | binding.lvChannel.adapter = channelAdapter
36 | map[groups[index]]?.let {
37 | channelAdapter?.setChannelList(it)
38 | binding.lvChannel.setSelection(it.indexOf(tv))
39 | }
40 | binding.lvGroup.setOnItemClickListener { _, _, i, _ ->
41 | map[groups[i]]?.let {
42 | channelAdapter?.setChannelList(it)
43 | binding.lvChannel.setSelection(0)
44 | }
45 | }
46 | binding.lvChannel.setOnItemClickListener { _, _, i, _ ->
47 | channelAdapter?.getChannelList()?.let {
48 | onChoose?.invoke(it[i])
49 | }
50 | dismiss()
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/dialog/SettingDialog.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.dialog
2 |
3 | import android.content.Intent
4 | import android.widget.Toast
5 | import androidx.lifecycle.lifecycleScope
6 | import com.tencent.mmkv.MMKV
7 | import com.ww.simpletv.AppUtils
8 | import com.ww.simpletv.ChannelUtils
9 | import com.ww.simpletv.Constant
10 | import com.ww.simpletv.FontSizeActivity
11 | import com.ww.simpletv.R
12 | import com.ww.simpletv.databinding.DialogSettingBinding
13 | import kotlinx.coroutines.CoroutineExceptionHandler
14 | import kotlinx.coroutines.Dispatchers
15 | import kotlinx.coroutines.launch
16 | import kotlinx.coroutines.withContext
17 | import java.io.File
18 |
19 | /**
20 | *
21 | * Copyright (C), 2024 Potato-66, All rights reserved.
22 | * 创建时间: 2024/2/28
23 | * @since 1.0
24 | * @version 1.0
25 | * @author Potato-66
26 | */
27 | class SettingDialog : BaseDialogFragment() {
28 | private var fontScale = MMKV.defaultMMKV().decodeFloat(Constant.KEY_FONT_SIZE, Constant.FONT_SIZE_NORMAL)
29 |
30 | override fun initLayoutResource(): Int = R.layout.dialog_setting
31 |
32 | override fun initBindData() {
33 | super.initBindData()
34 | binding.autoBoot = MMKV.defaultMMKV().decodeBool(Constant.KEY_BOOT_STARTUP, false)
35 | binding.autoUpdate = MMKV.defaultMMKV().decodeBool(Constant.KEY_AUTO_UPDATE, true)
36 | }
37 |
38 | override fun onResume() {
39 | super.onResume()
40 | binding.swAutoBoot.setOnCheckedChangeListener { _, b ->
41 | MMKV.defaultMMKV().encode(Constant.KEY_BOOT_STARTUP, b)
42 | }
43 | binding.swAutoUpdate.setOnCheckedChangeListener { _, b ->
44 | MMKV.defaultMMKV().encode(Constant.KEY_AUTO_UPDATE, b)
45 | }
46 | binding.btnManualUpdate.setOnClickListener {
47 | context?.let { context ->
48 | isCancelable = false
49 | binding.btnManualUpdate.text = getString(R.string.updating)
50 | binding.btnManualUpdate.isEnabled = false
51 | lifecycleScope.launch(CoroutineExceptionHandler { _, throwable ->
52 | binding.btnManualUpdate.text = getString(R.string.manual_update)
53 | binding.btnManualUpdate.isEnabled = true
54 | isCancelable = true
55 | Toast.makeText(
56 | context,
57 | "${R.string.update_file}:${throwable.message}",
58 | Toast.LENGTH_LONG
59 | ).show()
60 | }) {
61 | val b = withContext(Dispatchers.IO) {
62 | ChannelUtils.downloadIPTVFile(File(context.filesDir, Constant.FILE_NAME))
63 | ChannelUtils.updateChannel(context)
64 | }
65 | binding.btnManualUpdate.text = getString(R.string.manual_update)
66 | binding.btnManualUpdate.isEnabled = true
67 | isCancelable = true
68 | Toast.makeText(
69 | context,
70 | if (b) R.string.update_success else R.string.update_file,
71 | Toast.LENGTH_LONG
72 | ).show()
73 | }
74 | }
75 | }
76 | context?.let {
77 | binding.tvVersion.text = getString(R.string.version_info, AppUtils.getAppVersionName(it))
78 | }
79 | binding.btnUpdateVersion.setOnClickListener {
80 | activity?.let { context ->
81 | binding.btnUpdateVersion.text = getString(R.string.updating)
82 | binding.btnUpdateVersion.isEnabled = false
83 | lifecycleScope.launch {
84 | val versionInfo = withContext(Dispatchers.IO) {
85 | AppUtils.getVersion()
86 | }
87 | binding.btnUpdateVersion.text = getString(R.string.update_version)
88 | binding.btnUpdateVersion.isEnabled = true
89 | versionInfo?.let {
90 | if (it.versionCode > AppUtils.getAppVersionCode(context)) {
91 | UpdateDialog(it).show(context.supportFragmentManager, Constant.DIALOG_TAG_UPDATE)
92 | } else {
93 | Toast.makeText(context, getString(R.string.latest_version), Toast.LENGTH_LONG).show()
94 | }
95 | } ?: let {
96 | Toast.makeText(context, getString(R.string.get_version_error_hint), Toast.LENGTH_LONG).show()
97 | }
98 | }
99 | }
100 | }
101 | val fontScale = MMKV.defaultMMKV().decodeFloat(Constant.KEY_FONT_SIZE, Constant.FONT_SIZE_NORMAL)
102 | if (this.fontScale != fontScale) {
103 | this.fontScale = fontScale
104 | activity?.run {
105 | AppUtils.recreateActivity(this)
106 | startActivity(Intent(this,FontSizeActivity::class.java))
107 | }
108 | }
109 | binding.tvFontSize.text =
110 | when (fontScale) {
111 | Constant.FONT_SIZE_NORMAL -> getString(R.string.font_size_normal)
112 | Constant.FONT_SIZE_LARGE -> getString(R.string.font_size_large)
113 | else -> getString(R.string.font_size_huge)
114 | }
115 | binding.rlFontSize.setOnClickListener {
116 | context?.run {
117 | startActivity(Intent(this, FontSizeActivity::class.java))
118 | }
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ww/simpletv/dialog/UpdateDialog.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv.dialog
2 |
3 | import android.view.View
4 | import androidx.lifecycle.lifecycleScope
5 | import com.ww.simpletv.AppUtils
6 | import com.ww.simpletv.DownloadStatus
7 | import com.ww.simpletv.R
8 | import com.ww.simpletv.bean.VersionInfo
9 | import com.ww.simpletv.databinding.DialogUpdateBinding
10 | import kotlinx.coroutines.launch
11 |
12 | /**
13 | *
14 | * Copyright (C), 2024 Potato-66, All rights reserved.
15 | * 创建时间: 2024/3/19
16 | * @since 1.0
17 | * @version 1.0
18 | * @author Potato-66
19 | */
20 | class UpdateDialog(private val versionInfo: VersionInfo?) : BaseDialogFragment() {
21 | override fun initLayoutResource(): Int = R.layout.dialog_update
22 |
23 | override fun onResume() {
24 | super.onResume()
25 | isCancelable = false
26 | binding.btnCancel.setOnClickListener { dismiss() }
27 | binding.btnConfirm.setOnClickListener {
28 | binding.btnConfirm.visibility = View.GONE
29 | binding.btnCancel.visibility = View.GONE
30 | binding.btnCancel.isEnabled = false
31 | binding.btnCancel.text = getString(R.string.confirm)
32 | binding.progress.visibility = View.VISIBLE
33 | binding.tvHint.visibility = View.GONE
34 | context?.let { context ->
35 | lifecycleScope.launch {
36 | context.externalCacheDir?.let { dir ->
37 | AppUtils.download(dir, versionInfo).collect { status ->
38 | when (status) {
39 | is DownloadStatus.DownLoading -> {
40 | binding.progress.progress = status.progress
41 | }
42 |
43 | is DownloadStatus.Error -> {
44 | binding.progress.visibility = View.GONE
45 | binding.tvHint.visibility = View.VISIBLE
46 | binding.btnCancel.visibility = View.VISIBLE
47 | binding.tvHint.text =
48 | getString(R.string.update_fail_hint, "error:${status.error.message}")
49 | }
50 |
51 | is DownloadStatus.Fail -> {
52 | binding.progress.visibility = View.GONE
53 | binding.tvHint.visibility = View.VISIBLE
54 | binding.btnCancel.visibility = View.VISIBLE
55 | binding.tvHint.text =
56 | getString(R.string.update_fail_hint, "fail:${status.code}")
57 | }
58 |
59 | DownloadStatus.Success -> {
60 | dismiss()
61 | AppUtils.installApk(context)
62 | }
63 | }
64 | }
65 | }
66 | binding.btnCancel.isEnabled = true
67 | }
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/focus_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logo.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_focus_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/place_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/simple_tv_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/switch_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/switch_thumb_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/switch_track_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_font_size.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
27 |
28 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_player.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_channel.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
15 |
16 |
17 |
21 |
22 |
27 |
28 |
35 |
36 |
49 |
50 |
63 |
64 |
77 |
78 |
84 |
85 |
93 |
94 |
101 |
102 |
103 |
104 |
114 |
115 |
125 |
126 |
133 |
134 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_update.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
24 |
25 |
30 |
31 |
39 |
40 |
47 |
48 |
49 |
52 |
53 |
63 |
64 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_channel.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_font_size.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_group.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-anydpi-v26/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-hdpi/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-mdpi/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xhdpi/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxhdpi/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/bg_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxxhdpi/bg_window.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/font_size_normal
5 | - @string/font_size_large
6 | - @string/font_size_huge
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #FFFFFFFF
5 | #FFFFFFFF
6 | #242E35
7 | #234A6B
8 | #395456
9 | #70B3AC
10 | #808080
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 30sp
4 | 25sp
5 | 20sp
6 | 100dp
7 | 40dp
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SimpleTV
3 | 设置
4 | 开机自启
5 | 自动更新频道源
6 | 更新频道源
7 | 更新版本
8 | 当前版本:%1$S
9 | 当前已是最新版本
10 | 获取服务器版本信息失败,请稍后重试
11 | 检测到新版本,确认安装?
12 | 确认
13 | 取消
14 | 更新失败,请重试,%1$s
15 | 再按一次退出
16 | 更新成功
17 | 更新失败
18 | 检测更新…
19 | 解码失败,该频道不支持,请切换频道
20 | 播放失败,网络连接超时,请稍后重试
21 | 播放失败,请切换频道或退出重试
22 | 操作说明:\n 1)上下键快速切换频道,确认键弹出频道列表手动切换频道,菜单键弹出设置页面,连按两次返回退出播放。\n 2)打开开机自启开关后,电视开机会默认播放上次退出直播频道(部分设备由于系统限制,无法开机启动,请手动进入)。 \n 3)打开自动更新频道源后每24小时会自动更新,也可手动更新,黑屏无法播放时请尝试手动更新,更新完成后重新选择频道。
23 | 加载频道列表失败,请退出或更新频道源之后重试
24 | 初始化频道列表
25 | 初始化频道列表失败,请尝试手动更新或退出重试
26 | 更新频道列表失败,请尝试手动更新
27 | 字体大小
28 | 标准
29 | 大
30 | 超大
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
13 |
14 |
17 |
18 |
24 |
25 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ww/simpletv/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.ww.simpletv
2 |
3 | import org.junit.Test
4 |
5 | /**
6 | * Example local unit test, which will execute on the development machine (host).
7 | *
8 | * See [testing documentation](http://d.android.com/tools/testing).
9 | */
10 | class ExampleUnitTest {
11 | @Test
12 | fun addition_isCorrect() {
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.2.2" apply false
4 | id("org.jetbrains.kotlin.android") version "1.9.22" apply false
5 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 26 16:58:35 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/4.png
--------------------------------------------------------------------------------
/img/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/5.png
--------------------------------------------------------------------------------
/img/a1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/a1.png
--------------------------------------------------------------------------------
/img/control.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/control.jpg
--------------------------------------------------------------------------------
/img/q1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/q1.png
--------------------------------------------------------------------------------
/img/q2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/q2.png
--------------------------------------------------------------------------------
/img/telegram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Potato-66/SimpleTV/c5c6064b61ff0636385bde50f6fd58a5f78be195/img/telegram.png
--------------------------------------------------------------------------------
/m3u/ipv6/IPTV.m3u:
--------------------------------------------------------------------------------
1 | #EXTM3U
2 | #央视高清/19个频道
3 | #EXTINF:-1 tvg-id="A01" tvg-name="CCTV1" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV1.png" group-title="央视高清",CCTV-1 综合
4 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=reallive-cctv1&livemode=1&stbId=Shelly
5 | #EXTINF:-1 tvg-id="A02" tvg-name="CCTV2" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV2.png" group-title="央视高清",CCTV-2 财经
6 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=8878432746825677699&livemode=1&stbId=Shelly
7 | #EXTINF:-1 tvg-id="A03" tvg-name="CCTV3" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV3.png" group-title="央视高清",CCTV-3 综艺
8 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8378175926191110263&livemode=1&stbId=Shelly
9 | #EXTINF:-1 tvg-id="A04" tvg-name="CCTV4" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV4.png" group-title="央视高清",CCTV-4 中文国际
10 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=4719673858948437836&livemode=1&stbId=Shelly
11 | #EXTINF:-1 tvg-id="A05" tvg-name="CCTV5" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV5.png" group-title="央视高清",CCTV-5 体育
12 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=reallive-cctv5&livemode=1&stbId=Shelly
13 | #EXTINF:-1 tvg-id="A06" tvg-name="CCTV5+" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV5plus.png" group-title="央视高清",CCTV-5+ 体育赛事
14 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=reallive-cctv5plus&livemode=1&stbId=Shelly
15 | #EXTINF:-1 tvg-id="A07" tvg-name="CCTV6" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV6.png" group-title="央视高清",CCTV-6 电影
16 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6117099895556651277&livemode=1&stbId=Shelly
17 | #EXTINF:-1 tvg-id="A08" tvg-name="CCTV7" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV7.png" group-title="央视高清",CCTV-7 国防军事
18 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7050628689018054317&livemode=1&stbId=Shelly
19 | #EXTINF:-1 tvg-id="A09" tvg-name="CCTV8" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV8.png" group-title="央视高清",CCTV-8 电视剧
20 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8871488051501921127&livemode=1&stbId=Shelly
21 | #EXTINF:-1 tvg-id="A10" tvg-name="CCTV9" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV9.png" group-title="央视高清",CCTV-9 纪录
22 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=6350727550077620987&livemode=1&stbId=Shelly
23 | #EXTINF:-1 tvg-id="A11" tvg-name="CCTV10" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV10.png" group-title="央视高清",CCTV-10 科教
24 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226473/index.m3u8
25 | #EXTINF:-1 tvg-id="A12" tvg-name="CCTV11" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV11.png" group-title="央视高清",CCTV-11 戏曲
26 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=8983629816140945539&livemode=1&stbId=Shelly
27 | #EXTINF:-1 tvg-id="A13" tvg-name="CCTV12" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV12.png" group-title="央视高清",CCTV-12 社会与法
28 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5325631075193490169&livemode=1&stbId=Shelly
29 | #EXTINF:-1 tvg-id="A14" tvg-name="CCTV13" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV13.png" group-title="央视高清",CCTV-13 新闻
30 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=8593685226043367730&livemode=1&stbId=Shelly
31 | #EXTINF:-1 tvg-id="A15" tvg-name="CCTV14" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV14.png" group-title="央视高清",CCTV-14 少儿
32 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=6113730085955692619&livemode=1&stbId=Shelly
33 | #EXTINF:-1 tvg-id="A16" tvg-name="CCTV15" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV15.png" group-title="央视高清",CCTV-15 音乐
34 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=4832596595465032187&livemode=1&stbId=Shelly
35 | #EXTINF:-1 tvg-id="A17" tvg-name="CCTV16" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV16.png" group-title="央视高清",CCTV-16 奥林匹克
36 | http://[2409:8087:1a01:df::7005]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226100/index.m3u8
37 | #EXTINF:-1 tvg-id="A18" tvg-name="CCTV17" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV17.png" group-title="央视高清",CCTV-17 农业农村
38 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=1000000005000056836&livemode=1&stbId=Shelly
39 | #EXTINF:-1 tvg-id="A19" tvg-name="CCTV4K" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTV4K.png" group-title="央视高清",CCTV-4K 超高清
40 | http://hlslive-tx-cdn.ysp.cctv.cn/ysp/2024081302.m3u8?key=txiptv&playlive=1&authid=0
41 |
42 | #卫视高清/40个频道
43 | #EXTINF:-1 tvg-id="B01" tvg-name="湖南卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan.png" group-title="卫视高清",湖南卫视
44 | http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225610/index.m3u8
45 | #EXTINF:-1 tvg-id="B02" tvg-name="浙江卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang.png" group-title="卫视高清",浙江卫视
46 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=4945391150963996912&livemode=1&stbId=Shelly
47 | #EXTINF:-1 tvg-id="B03" tvg-name="东方卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Dongfang.png" group-title="卫视高清",东方卫视
48 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=6690661682550849647&livemode=1&stbId=Shelly
49 | #EXTINF:-1 tvg-id="B04" tvg-name="北京卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Beijing.png" group-title="卫视高清",北京卫视
50 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8186688740528366039&livemode=1&stbId=Shelly
51 | #EXTINF:-1 tvg-id="B05" tvg-name="江苏卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Jiangsu.png" group-title="卫视高清",江苏卫视
52 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=4796340999304424198&livemode=1&stbId=Shelly
53 | #EXTINF:-1 tvg-id="B06" tvg-name="安徽卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Anhui.png" group-title="卫视高清",安徽卫视
54 | http://[2409:8087:1a01:df::4077]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8
55 | #EXTINF:-1 tvg-id="B07" tvg-name="重庆卫视" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/Chongqing.png" group-title="卫视高清",重庆卫视
56 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=4995134319251569870&livemode=1&stbId=Shelly
57 | #EXTINF:-1 tvg-id="B08" tvg-name="东南卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Dongnan.png" group-title="卫视高清",东南卫视
58 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7721801662260370363&livemode=1&stbId=Shelly
59 | #EXTINF:-1 tvg-id="B09" tvg-name="甘肃卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Gansu.png" group-title="卫视高清",甘肃卫视
60 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8938598377512027715&livemode=1&stbId=Shelly
61 | #EXTINF:-1 tvg-id="B10" tvg-name="广东卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Guangdong.png" group-title="卫视高清",广东卫视
62 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=wasusyt&Contentid=5306115082857917057&livemode=1&stbId=Shelly
63 | #EXTINF:-1 tvg-id="B11" tvg-name="广西卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Guangxi.png" group-title="卫视高清",广西卫视
64 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8069815772565148726&livemode=1&stbId=Shelly
65 | #EXTINF:-1 tvg-id="B12" tvg-name="贵州卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Guizhou.png" group-title="卫视高清",贵州卫视
66 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5468064193211864646&livemode=1&stbId=Shelly
67 | #EXTINF:-1 tvg-id="B13" tvg-name="海南卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hainan.png" group-title="卫视高清",海南卫视
68 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7899627377857656087&livemode=1&stbId=Shelly
69 | #EXTINF:-1 tvg-id="B14" tvg-name="河北卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hebei.png" group-title="卫视高清",河北卫视
70 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8360637122634561354&livemode=1&stbId=Shelly
71 | #EXTINF:-1 tvg-id="B15" tvg-name="黑龙江卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang.png" group-title="卫视高清",黑龙江卫视
72 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8467838254824392379&livemode=1&stbId=Shelly
73 | #EXTINF:-1 tvg-id="B16" tvg-name="河南卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Henan.png" group-title="卫视高清",河南卫视
74 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7304224304099543804&livemode=1&stbId=Shelly
75 | #EXTINF:-1 tvg-id="B17" tvg-name="湖北卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei.png" group-title="卫视高清",湖北卫视
76 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6956052407855047826&livemode=1&stbId=Shelly
77 | #EXTINF:-1 tvg-id="B18" tvg-name="江西卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Jiangxi.png" group-title="卫视高清",江西卫视
78 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5616078396620462481&livemode=1&stbId=Shelly
79 | #EXTINF:-1 tvg-id="B19" tvg-name="吉林卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Jilin.png" group-title="卫视高清",吉林卫视
80 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6042486520432105075&livemode=1&stbId=Shelly
81 | #EXTINF:-1 tvg-id="B20" tvg-name="辽宁卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Liaoning.png" group-title="卫视高清",辽宁卫视
82 | http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8
83 | #EXTINF:-1 tvg-id="B21" tvg-name="山东卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shandong.png" group-title="卫视高清",山东卫视
84 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7347081113971056899&livemode=1&stbId=Shelly
85 | #EXTINF:-1 tvg-id="B22" tvg-name="深圳卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shenzhen.png" group-title="卫视高清",深圳卫视
86 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5352218613763113657&livemode=1&stbId=Shelly
87 | #EXTINF:-1 tvg-id="B23" tvg-name="四川卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Sichuan.png" group-title="卫视高清",四川卫视
88 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8744805262559782299&livemode=1&stbId=Shelly
89 | #EXTINF:-1 tvg-id="B24" tvg-name="天津卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Tianjin.png" group-title="卫视高清",天津卫视
90 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6707942579111517572&livemode=1&stbId=Shelly
91 | #EXTINF:-1 tvg-id="B25" tvg-name="云南卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Yunnan.png" group-title="卫视高清",云南卫视
92 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8567124785369040339&livemode=1&stbId=Shelly
93 | #EXTINF:-1 tvg-id="B26" tvg-name="新疆卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Xinjiang.png" group-title="卫视高清",新疆卫视
94 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5802765535081445534&livemode=1&stbId=Shelly
95 | #EXTINF:-1 tvg-id="B27" tvg-name="三沙卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Sansha.png" group-title="卫视高清",三沙卫视
96 | http://[2409:8087:5e00:24::1e]:6060/000000001000/4600001000000000117/1.m3u8
97 | #EXTINF:-1 tvg-id="B28" tvg-name="青海卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Qinghai.png" group-title="卫视高清",青海卫视
98 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6721180114242078041&livemode=1&stbId=Shelly
99 | #EXTINF:-1 tvg-id="B29" tvg-name="陕西卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi.png" group-title="卫视高清",陕西卫视
100 | http://[2409:8087:5e00:24::1e]:6060/000000001000/1000000002000007495/1.m3u8
101 | #EXTINF:-1 tvg-id="B30" tvg-name="山西卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi_.png" group-title="卫视高清",山西卫视
102 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=4821787565788875765&livemode=1&stbId=Shelly
103 | #EXTINF:-1 tvg-id="B31" tvg-name="西藏卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Xizang.png" group-title="卫视高清",西藏卫视
104 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6603041244077933770&livemode=1&stbId=Shelly
105 | #EXTINF:-1 tvg-id="B32" tvg-name="内蒙古卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Neimeng.png" group-title="卫视高清",内蒙古卫视
106 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=5559214655508729688&livemode=1&stbId=Shelly
107 | #EXTINF:-1 tvg-id="B33" tvg-name="宁夏卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Ningxia.png" group-title="卫视高清",宁夏卫视
108 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=7527747602951312973&livemode=1&stbId=Shelly
109 | #EXTINF:-1 tvg-id="B34" tvg-name="兵团卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Bingtuan.png" group-title="卫视高清",兵团卫视
110 | http://[2409:8087:1a0b:df::4020]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226439/index.m3u8
111 | #EXTINF:-1 tvg-id="B35" tvg-name="安多卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Anduo.png" group-title="卫视高清",安多卫视
112 | http://[2409:8087:1a0b:df::4007]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225659/index.m3u8
113 | #EXTINF:-1 tvg-id="B36" tvg-name="康巴卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Kangba.png" group-title="卫视高清",康巴卫视
114 | http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225660/index.m3u8
115 | #EXTINF:-1 tvg-id="B37" tvg-name="大湾区卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Guangdong10.png" group-title="卫视高清",大湾区卫视
116 | http://[2409:8087:5e00:24::1e]:6060/000000001000/1000000002000011619/index.m3u8
117 | #EXTINF:-1 tvg-id="B38" tvg-name="农林卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Nonglin.png" group-title="卫视高清",农林卫视
118 | http://[2409:8087:74d9:21::6]/270000001128/9900000060/index.m3u8
119 | #EXTINF:-1 tvg-id="B39" tvg-name="延边卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Yanbian.png" group-title="卫视高清",延边卫视
120 | http://[2409:8087:1a0b:df::4020]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226516/index.m3u8
121 | #EXTINF:-1 tvg-id="B40" tvg-name="厦门卫视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Xiamen.png" group-title="卫视高清",厦门卫视
122 | http://[2409:8087:5e00:24::1e]:6060/000000001000/1000000005000266006/1.m3u8
123 |
124 | #数字高清/40个频道
125 | #EXTINF:-1 tvg-id="CHC1" tvg-name="CHC影迷电影" tvg-logo="https://live.fanmingming.com/tv/CHC影迷电影.png" group-title="数字高清",CHC影迷电影
126 | http://cg14.hunancatv.cn:9090/live/CHCGQ_8000.m3u8?deviceid=866265039837531&sid=Yp6brv0MWYukAl4oJr9yrw%3D%3D&nonce=Ri1TgJg9dlAK&acl=0111&hlsSessionID=11082234455604315487&srchost=172.16.22.158:8060&gid=QTVto%2BZu6pkkcNYy4OxcMg%3D%3D
127 | #EXTINF:-1 tvg-id="CHC2" tvg-name="CHC家庭影院" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CHC2.png" group-title="数字高清",CHC家庭影院
128 | http://cg11.hunancatv.cn:9090/live/CHCJTYY_8000.m3u8?deviceid=866265039837531&sid=Yp6brv0MWYukAl4oJr9yrw%3D%3D&nonce=Ri1TgJg9dlAK&acl=0111&hlsSessionID=11082234455604316833&srchost=172.16.22.158:8060&gid=c%2BNqgq8pR1ZQ7ZzcmkS8%2BQ%3D%3D
129 | #EXTINF:-1 tvg-id="CHC3" tvg-name="CHC动作电影" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CHC3.png" group-title="数字高清",CHC动作电影
130 | http://cg0.hunancatv.cn:9090/live/CHCDZDY_8000.m3u8?deviceid=866265039837531&sid=Yp6brv0MWYukAl4oJr9yrw%3D%3D&nonce=Ri1TgJg9dlAK&acl=0111&hlsSessionID=11082234455604316076&srchost=172.16.22.158:8060&gid=ZF3q5L%2FmYWch1qWqTIPAjw%3D%3D
131 | #EXTINF:-1 tvg-id="Qiusuo1" tvg-name="求索纪录" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Qiusuo1.png" group-title="数字高清",求索纪录
132 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000004000000010/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000004000000010&IASHttpSessionId=OTT19019320240419154124000281
133 | #EXTINF:-1 tvg-id="Qiusuo2" tvg-name="求索科学" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Qiusuo2.png" group-title="数字高清",求索科学
134 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000004000000011/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000004000000011&IASHttpSessionId=OTT19019320240419154124000281
135 | #EXTINF:-1 tvg-id="Qiusuo3" tvg-name="求索动物" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Qiusuo3.png" group-title="数字高清",求索动物
136 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000004000000009/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000004000000009&IASHttpSessionId=OTT19019320240419154124000281
137 | #EXTINF:-1 tvg-id="Qiusuo4" tvg-name="求索生活" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Qiusuo4.png" group-title="数字高清",求索生活
138 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000004000000008/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000004000000008&IASHttpSessionId=OTT19019320240419154124000281
139 | #EXTINF:-1 tvg-id="hmdy" tvg-name="黑莓电影" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/hmdy.png" group-title="数字高清",黑莓电影
140 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=8785669936177902664&livemode=1&stbId=Shelly
141 | #EXTINF:-1 tvg-id="hmdh" tvg-name="黑莓动画" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/hmdh.png" group-title="数字高清",黑莓动画
142 | http://gslbserv.itv.cmvideo.cn/index.m3u8?channel-id=ystenlive&Contentid=6497762188035533951&livemode=1&stbId=Shelly
143 | #EXTINF:-1 tvg-id="dbdj" tvg-name="哒啵电竞" tvg-logo="https://live.fanmingming.com/tv/哒啵电竞.png" group-title="数字高清",哒啵电竞
144 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000003000000066/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000003000000066&IASHttpSessionId=OTT19019320240419154124000281
145 | #EXTINF:-1 tvg-id="dbss" tvg-name="哒啵赛事" tvg-logo="https://live.fanmingming.com/tv/哒啵赛事.png" group-title="数字高清",哒啵赛事
146 | http://[2409:8087:1a01:df::7005]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225675/index.m3u8
147 | #EXTINF:-1 tvg-id="leyou" tvg-name="乐游" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/leyou.png" group-title="数字高清",乐游
148 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031112/1.m3u8
149 | #EXTINF:-1 tvg-id="jsrw" tvg-name="纪实人文" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/jsrw.png" group-title="数字高清",纪实人文
150 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225738/index.m3u8
151 | #EXTINF:-1 tvg-id="xc4k" tvg-name="纯享4K" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/cx4k.png" group-title="数字高清",纯享4K
152 | http://[2409:8087:5e00:24::1e]:6060/000000001000/1000000004000011651/1.m3u8
153 | #EXTINF:-1 tvg-id="as4K" tvg-name="爱上4K" tvg-logo="https://live.fanmingming.com/tv/爱上4K.png" group-title="数字高清",爱上4K
154 | http://[2409:8087:5e01:34::23]:6610/ZTE_CMS/00000001000000060000000000000459/index.m3u8?IAS
155 | #EXTINF:-1 tvg-id="CCTVfyjc" tvg-name="风云剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVfyjc.png" group-title="数字高清",风云剧场
156 | http://39.134.18.195:80/dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8
157 | #EXTINF:-1 tvg-id="CCTVfyyy" tvg-name="风云音乐" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVfyyy.png" group-title="数字高清",风云音乐
158 | http://39.134.18.195:80/dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226953/index.m3u8
159 | #EXTINF:-1 tvg-id="CCTVdyjc" tvg-name="第一剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVdyjc.png" group-title="数字高清",第一剧场
160 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN638077f5ca035/1.m3u8?m3u8_level=2
161 | #EXTINF:-1 tvg-id="CCTVnxss" tvg-name="女性时尚" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVnxss.png" group-title="数字高清",女性时尚
162 | http://[2409:8087:7001:20:2::3]:80/dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226969/index.m3u8
163 | #EXTINF:-1 tvg-id="CCTVfyzq" tvg-name="风云足球" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVfyzq.png" group-title="数字高清",风云足球
164 | http://111.61.236.247:9081/hls/58/index.m3u8
165 | #EXTINF:-1 tvg-id="CCTVbqkj" tvg-name="兵器科技" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVbqkj.png" group-title="数字高清",兵器科技
166 | http://111.61.236.247:9081/hls/61/index.m3u8
167 | #EXTINF:-1 tvg-id="CCTVhjjc" tvg-name="怀旧剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVhjjc.png" group-title="数字高清",怀旧剧场
168 | http://111.61.236.247:9081/hls/67/index.m3u8
169 | #EXTINF:-1 tvg-id="CCTVsjdl" tvg-name="世界地理" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVsjdl.png" group-title="数字高清",世界地理
170 | http://111.61.236.247:9081/hls/62/index.m3u8
171 | #EXTINF:-1 tvg-id="CCTVyswhjp" tvg-name="文化精品" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVyswhjp.png" group-title="数字高清",文化精品
172 | https://tv.iill.top/live/whjp
173 | #EXTINF:-1 tvg-id="CCTVystq" tvg-name="央视台球" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVystq.png" group-title="数字高清",央视台球
174 | http://222.169.41.210:9901/tsfile/live/1012_1.m3u8?key=txiptv&playlive=1&authid=0
175 | #EXTINF:-1 tvg-id="CCTVgefwq" tvg-name="高尔夫网球" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVgefwq.png" group-title="数字高清",高尔夫网球
176 | http://[2409:8087:74d9:21::6]/270000001128/9900000094/index.m3u8
177 | #EXTINF:-1 tvg-id="CCTVdszn" tvg-name="电视指南" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/CCTVdszn.png" group-title="数字高清",电视指南
178 | http://39.134.18.195:80/dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8
179 | #EXTINF:-1 tvg-id="dsjc" tvg-name="都市剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/dsjc.png" group-title="数字高清",都市剧场
180 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031111/1.m3u8
181 | #EXTINF:-1 tvg-id="jsxt" tvg-name="金色学堂" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/jsxt.png" group-title="数字高清",金色学堂
182 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000010000026105/1.m3u8
183 | #EXTINF:-1 tvg-id="hhxd" tvg-name="哈哈炫动" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/hhxd.png" group-title="数字高清",哈哈炫动
184 | http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225657/index.m3u8
185 | #EXTINF:-1 tvg-id="yxfy" tvg-name="游戏风云" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/yxfy.png" group-title="数字高清",游戏风云
186 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031114/1.m3u8
187 | #EXTINF:-1 tvg-id="hxjc" tvg-name="欢笑剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/hxjc4k.png" group-title="数字高清",欢笑剧场
188 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000002000009455/1.m3u8
189 | #EXTINF:-1 tvg-id="qcxj" tvg-name="七彩戏剧" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/qcxj.png" group-title="数字高清",七彩戏剧
190 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000010/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000010&IASHttpSessionId=OTT19019320240419154124000281
191 | #EXTINF:-1 tvg-id="dycj" tvg-name="第一财经" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/dycj.png" group-title="数字高清",第一财经
192 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000004/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000004&IASHttpSessionId=OTT19019320240419154124000281
193 | #EXTINF:-1 tvg-id="wssj" tvg-name="武术世界" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/wssj.png" group-title="数字高清",武术世界
194 | http://111.61.236.247:9081/hls/60/index.m3u8
195 | #EXTINF:-1 tvg-id="wwbk" tvg-name="文物宝库" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/wwbk.png" group-title="数字高清",文物宝库
196 | http://111.61.236.247:9081/hls/64/index.m3u8
197 | #EXTINF:-1 tvg-id="lypd" tvg-name="梨园频道" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/lypd.png" group-title="数字高清",梨园频道
198 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN6380788ba7bed/index.m3u8?AuthInfo=toEYVdLfxymUP2l9NZpQI5%2BK6T7j%2FlRm%2BvbM9VO7bA0q1S1k1f36SqqriM0FZoFSAJRfCt8SS7X6sTRmXb81a8O4H%2FdroDKjLoDeaMQdyJQ
199 | #EXTINF:-1 tvg-id="cwjd" tvg-name="重温经典" tvg-logo="https://live.fanmingming.com/tv/重温经典.png" group-title="数字高清",重温经典
200 | http://ali.hlspull.yximgs.com/live/awei_cwjd.flv
201 | #EXTINF:-1 tvg-id="jbty" tvg-name="劲爆体育" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/jbty.png" group-title="数字高清",劲爆体育
202 | http://[2409:8087:7000:20:1000::22]:6060/000000001000/5000000002000029972/index.m3u8?channel-id=bestzb&Contentid=5000000002000029972&livemode=1&stbId=3
203 | #EXTINF:-1 tvg-id="tywq" tvg-name="天元围棋" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/tywq.png" group-title="数字高清",天元围棋
204 | https://itv.lat/hls?channel=tywq_2000&token=fbca23599065f5bd644b330db4e3b1f5
205 | #EXTINF:-1 tvg-id="ytcq" tvg-name="弈坛春秋" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/ytcq.png" group-title="数字高清",弈坛春秋
206 | http://[2409:8087:7004:20:1000::22]:6610/yinhe/2/ch00000090990000001322/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
207 |
208 | #国际频道/20个频道
209 | #EXTINF:-1 tvg-id="PH01" tvg-name="凤凰中文" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Phoenix1.png" group-title="国际时事",凤凰中文
210 | http://aktv.top/AKTV/live/aktv/null-3/AKTV.m3u8
211 | #EXTINF:-1 tvg-id="PH02" tvg-name="凤凰资讯" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Phoenix2.png" group-title="国际时事",凤凰资讯
212 | http://aktv.top/AKTV/live/aktv/null-4/AKTV.m3u8
213 | #EXTINF:-1 tvg-id="PH03" tvg-name="凤凰香港" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Phoenix3.png" group-title="国际时事",凤凰香港
214 | http://aktv.top/AKTV/live/aktv/null-5/AKTV.m3u8
215 | #EXTINF:-1 tvg-id="NHK" tvg-name="NHK World" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NHKWorld.png" group-title="国际时事",NHK World
216 | https://nhkwlive-ojp.akamaized.net/hls/live/2003459/nhkwlive-ojp-en/index.m3u8
217 | #EXTINF:-1 tvg-id="CNA" tvg-name="CNA" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/ChannelAsia.png" group-title="国际时事",CNA
218 | https://d2e1asnsl7br7b.cloudfront.net/7782e205e72f43aeb4a48ec97f66ebbe/index_5.m3u8
219 |
220 | #湖南/14个频道
221 | #EXTINF:-1 tvg-id="Cpd" tvg-name="茶频道" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/chapd.png" group-title="地方特色",茶频道
222 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031209/1.m3u8
223 | #EXTINF:-1 tvg-id="Klcd" tvg-name="快乐垂钓" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/klcd.png" group-title="地方特色",快乐垂钓
224 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031206/1.m3u8
225 | #EXTINF:-1 tvg-id="Jyjs" tvg-name="金鹰纪实" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/jyjs.png" group-title="地方特色",金鹰纪实
226 | http://[2409:8087:5e00:24::1e]:6060/000000001000/5000000011000031203/1.m3u8
227 | #EXTINF:-1 tvg-id="Jykt" tvg-name="金鹰卡通" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/jykt.png" group-title="地方特色",金鹰卡通
228 | http://[2409:8087:1a0b:df::4008]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225653/index.m3u8
229 | #EXTINF:-1 tvg-id="Xfpy" tvg-name="先锋乒羽" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/xfpy.png" group-title="地方特色",先锋乒羽
230 | http://zy.otioi.cn/live/live.php?token=jh6ITHRn&id=xfpy
231 | #EXTINF:-1 tvg-id="HN01" tvg-name="湖南经视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan1.png" group-title="地方特色",湖南经视
232 | http://222.241.154.37:9901/tsfile/live/23020_1.m3u8
233 | #EXTINF:-1 tvg-id="HN02" tvg-name="湖南都市" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan2.png" group-title="地方特色",湖南都市
234 | http://222.241.154.37:9901/tsfile/live/23021_1.m3u8
235 | #EXTINF:-1 tvg-id="HN03" tvg-name="湖南爱晚" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan3.png" group-title="地方特色",湖南爱晚
236 | http://222.241.154.37:9901/tsfile/live/23022_1.m3u8
237 | #EXTINF:-1 tvg-id="HN04" tvg-name="湖南国际" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan4.png" group-title="地方特色",湖南国际
238 | http://222.241.154.37:9901/tsfile/live/23024_1.m3u8
239 | #EXTINF:-1 tvg-id="HN05" tvg-name="湖南娱乐" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan5.png" group-title="地方特色",湖南娱乐
240 | http://222.241.154.37:9901/tsfile/live/23023_1.m3u8
241 | #EXTINF:-1 tvg-id="HN06" tvg-name="湖南电影" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan6.png" group-title="地方特色",湖南电影
242 | http://222.241.154.37:9901/tsfile/live/23025_1.m3u8
243 | #EXTINF:-1 tvg-id="HN07" tvg-name="湖南电视剧" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hunan7.png" group-title="地方特色",湖南电视剧
244 | http://222.241.154.37:9901/tsfile/live/23026_1.m3u8
245 | #EXTINF:-1 tvg-id="HN08" tvg-name="长沙新闻" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/mgtv@main/logo/csxw.png" group-title="地方特色",长沙新闻
246 | https://mirror.ghproxy.com/https://raw.githubusercontent.com/Meroser/mgtv/main/m3u8/hn14.m3u8
247 | #EXTINF:-1 tvg-id="HN09" tvg-name="长沙政法" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/mgtv@main/logo/cszf.png" group-title="地方特色",长沙政法
248 | https://mirror.ghproxy.com/https://raw.githubusercontent.com/Meroser/mgtv/main/m3u8/hn15.m3u8
249 | #EXTINF:-1 tvg-id="HN10" tvg-name="长沙女性" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/mgtv@main/logo/csnx.png" group-title="地方特色",长沙女性
250 | https://mirror.ghproxy.com/https://raw.githubusercontent.com/Meroser/mgtv/main/m3u8/hn16.m3u8
251 |
252 | #山西/5个频道
253 | #EXTINF:-1 tvg-id="SX01" tvg-name="黄河电视台" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV-DF@main/logo/Shanxi1.png" group-title="地方特色",黄河电视台
254 | https://mirror.ghproxy.com/https://raw.githubusercontent.com/Meroser/IPTV-DF/main/m3u8/shanxi/shanxi1.m3u8
255 | #EXTINF:-1 tvg-id="SX02" tvg-name="山西经济与科技" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV-DF@main/logo/Shanxi2.png" group-title="地方特色",山西经济与科技
256 | http://liveflash.sxrtv.com/live/sxfinance.m3u8?sub_m3u8=true&edge_slice=true
257 | #EXTINF:-1 tvg-id="SX03" tvg-name="山西影视" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV-DF@main/logo/Shanxi3.png" group-title="地方特色",山西影视
258 | http://liveflash.sxrtv.com/live/sxfilm.m3u8?sub_m3u8=true&edge_slice=true
259 | #EXTINF:-1 tvg-id="SX04" tvg-name="山西社会与法治" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV-DF@main/logo/Shanxi4.png" group-title="地方特色",山西社会与法治
260 | http://liveflash.sxrtv.com/live/sxedu.m3u8?sub_m3u8=true&edge_slice=true
261 | #EXTINF:-1 tvg-id="SX05" tvg-name="山西文体生活" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV-DF@main/logo/Shanxi5.png" group-title="地方特色",山西文体生活
262 | http://liveflash.sxrtv.com/live/sxpublic.m3u8?sub_m3u8=true&edge_slice=true
263 |
264 | #湖北/7个频道
265 | #EXTINF:-1 tvg-id="HB01" tvg-name="湖北综合" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei1.png" group-title="地方特色",湖北综合
266 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN638072216f7da/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNLx3R3LdIBdA5q61lut1LwZcw4SC2tJubc%2Bq5t%2F61jvWCtjrJL0x%2BHncPgdmcbBW9I
267 | #EXTINF:-1 tvg-id="HB02" tvg-name="湖北影视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei2.png" group-title="地方特色",湖北影视
268 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN6380728abccd2/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNKU1xiiKGJYFBZK9RpEkaggogtHJZW6nJQqLppbEYE%2BqJ9kJW%2FMvKldfS2dCii9VSI
269 | #EXTINF:-1 tvg-id="HB03" tvg-name="湖北教育" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei3.png" group-title="地方特色",湖北教育
270 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN638072a6cbbf6/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNKTFQuOKNd5i0HMa3ydMWIQ6kJ2fBJGJ77BLTXK72CqUYQsX3ZguYt0954s9hfZTfc
271 | #EXTINF:-1 tvg-id="HB04" tvg-name="湖北生活" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei4.png" group-title="地方特色",湖北生活
272 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN638072c62e405/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNKJPodw5BcDirSOw7egztI5bUvJjqPzSNAhrA37GrbRBIAjGQpzP0QmgsmuHnQyd2c
273 | #EXTINF:-1 tvg-id="HB05" tvg-name="湖北公共" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei5.png" group-title="地方特色",湖北公共
274 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN6380726d0ee3b/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNKgjpyhD%2FJd6SZjkcHAKXbsjwotAY57dianC4%2ByY7artm7J7F4GFO6G99D0bYMJRxI
275 | #EXTINF:-1 tvg-id="HB06" tvg-name="湖北经视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei6.png" group-title="地方特色",湖北经视
276 | http://59.173.243.210:9901/tsfile/live/1019_1.m3u8?key=txiptv&playlive=1&authid=0
277 | #EXTINF:-1 tvg-id="HB07" tvg-name="湖北垄上" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Hubei7.png" group-title="地方特色",湖北垄上
278 | http://[2409:8087:4c0a:22:1::11]:6410/170000001115/UmaiCHAN638072463c333/index.m3u8?AuthInfo=9kOOdBn7MFF%2F2bWjKgahUUAdKgLqNB5aOAUhcp5CeNL%2Be1h4FfA5QqwJ7IPOz3p2qIS7djSxm17Htd%2FckZkm%2FVqsKHEJFJt26ju9iibzMEo
279 |
280 | #浙江/7个频道
281 | #EXTINF:-1 tvg-id="ZJ01" tvg-name="浙江钱江都市" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang1.png" group-title="地方特色",浙江钱江都市
282 | https://ali-m-l.cztv.com/channels/lantian/channel002/1080p.m3u8
283 | #EXTINF:-1 tvg-id="ZJ02" tvg-name="浙江经济生活" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang2.png" group-title="地方特色",浙江经济生活
284 | https://ali-m-l.cztv.com/channels/lantian/channel003/1080p.m3u8
285 | #EXTINF:-1 tvg-id="ZJ03" tvg-name="浙江教科影视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang3.png" group-title="地方特色",浙江教科影视
286 | https://ali-m-l.cztv.com/channels/lantian/channel004/1080p.m3u8
287 | #EXTINF:-1 tvg-id="ZJ04" tvg-name="浙江民生休闲" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang4.png" group-title="地方特色",浙江民生休闲
288 | https://ali-m-l.cztv.com/channels/lantian/channel006/1080p.m3u8
289 | #EXTINF:-1 tvg-id="ZJ05" tvg-name="浙江公共新闻" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang5.png" group-title="地方特色",浙江公共新闻
290 | https://ali-m-l.cztv.com/channels/lantian/channel007/1080p.m3u8
291 | #EXTINF:-1 tvg-id="ZJ06" tvg-name="浙江少儿" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang6.png" group-title="地方特色",浙江少儿
292 | https://ali-m-l.cztv.com/channels/lantian/channel008/1080p.m3u8
293 | #EXTINF:-1 tvg-id="ZJ07" tvg-name="浙江国际" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Zhejiang7.png" group-title="地方特色",浙江国际
294 | https://ali-m-l.cztv.com/channels/lantian/channel010/1080p.m3u8
295 |
296 |
297 | #上海/6个频道
298 | #EXTINF:-1 tvg-id="SH01" tvg-name="上海新闻" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanghai1.png" group-title="地方特色",上海新闻
299 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000005/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000005&IASHttpSessionId=OTT19019320240419154124000281
300 | #EXTINF:-1 tvg-id="SH02" tvg-name="上海都市" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanghai2.png" group-title="地方特色",上海都市
301 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000012/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000012&IASHttpSessionId=OTT19019320240419154124000281
302 | #EXTINF:-1 tvg-id="SH03" tvg-name="东方影视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanghai3.png" group-title="地方特色",东方影视
303 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000013/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000013&IASHttpSessionId=OTT19019320240419154124000281
304 | #EXTINF:-1 tvg-id="SH04" tvg-name="上海外语" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanghai4.png" group-title="地方特色",上海外语
305 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000001/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000001&IASHttpSessionId=OTT19019320240419154124000281
306 | #EXTINF:-1 tvg-id="SH05" tvg-name="五星体育" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanghai5.png" group-title="地方特色",五星体育
307 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000007/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000007&IASHttpSessionId=OTT19019320240419154124000281
308 | #EXTINF:-1 tvg-id="SH06" tvg-name="东方财经" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/dfcj.png" group-title="地方特色",东方财经
309 | http://[2409:8087:5e01:24::16]:6610/000000001000/2000000002000000090/index.m3u8?stbId=3&livemode=1&HlsProfileId=&channel-id=hnbblive&Contentid=2000000002000000090&IASHttpSessionId=OTT19019320240419154124000281
310 |
311 | #北京/2个频道
312 | #EXTINF:-1 tvg-id="BJ05" tvg-name="纪实科教" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Beijing5.png" group-title="地方特色",纪实科教
313 | http://[2409:8087:1a01:df::7005]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225729/index.m3u8
314 | #EXTINF:-1 tvg-id="BJ08" tvg-name="卡酷少儿" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Beijing8.png" group-title="地方特色",卡酷少儿
315 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225654/index.m3u8
316 |
317 | #陕西/7个频道
318 | #EXTINF:-1 tvg-id="I01" tvg-name="陕西新闻资讯" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi1.png" group-title="地方特色",陕西新闻资讯
319 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001719/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
320 | #EXTINF:-1 tvg-id="I02" tvg-name="陕西都市青春" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi2.png" group-title="地方特色",陕西都市青春
321 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001593/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
322 | #EXTINF:-1 tvg-id="I03" tvg-name="陕西生活" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi3.png" group-title="地方特色",陕西生活
323 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001729/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
324 | #EXTINF:-1 tvg-id="I04" tvg-name="陕西影视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi4.png" group-title="地方特色",陕西影视
325 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001730/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
326 | #EXTINF:-1 tvg-id="I05" tvg-name="陕西公共" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi5.png" group-title="地方特色",陕西公共
327 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001739/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
328 | #EXTINF:-1 tvg-id="I06" tvg-name="陕西体育休闲" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi6.png" group-title="地方特色",陕西体育休闲
329 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001594/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
330 | #EXTINF:-1 tvg-id="I07" tvg-name="陕西西部电影" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Shanxi7.png" group-title="地方特色",陕西西部电影
331 | http://[2409:8087:7000:20:1000::22]:6060/yinhe/2/ch00000090990000001749/index.m3u8?virtualDomain=yinhe.live_hls.zte.com
332 |
333 | #黑龙江/6个频道
334 | #EXTINF:-1 tvg-id="HLJ01" tvg-name="黑龙江新闻法治" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang1.png" group-title="地方特色",黑龙江新闻法治
335 | http://[2409:8087:1a01:df::4036]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226330/1.m3u8
336 | #EXTINF:-1 tvg-id="HLJ02" tvg-name="黑龙江文体" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang2.png" group-title="地方特色",黑龙江文体
337 | http://[2409:8087:1a01:df::4025]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226239/1.m3u8
338 | #EXTINF:-1 tvg-id="HLJ03" tvg-name="黑龙江影视" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang3.png" group-title="地方特色",黑龙江影视
339 | http://[2409:8087:1a01:df::7005]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226298/index.m3u8
340 | #EXTINF:-1 tvg-id="HLJ04" tvg-name="黑龙江都市" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang4.png" group-title="地方特色",黑龙江都市
341 | http://[2409:8087:1a01:df::4077]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226301/index.m3u8
342 | #EXTINF:-1 tvg-id="HLJ05" tvg-name="黑龙江公共农村" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang5.png" group-title="地方特色",黑龙江公共农村
343 | http://[2409:8087:1a01:df::4077]/PLTV/88888888/224/3221225994/index.m3u8
344 | #EXTINF:-1 tvg-id="HLJ06" tvg-name="黑龙江少儿" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/Heilongjiang6.png" group-title="地方特色",黑龙江少儿
345 | http://[2409:8087:1a01:df::7005]/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226304/index.m3u8
346 |
347 | #NewTV/15个频道
348 | #EXTINF:-1 tvg-id="N01" tvg-name="NewTV军事评论" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV01.png" group-title="NewTV",NewTV军事评论
349 | http://[2409:8087:1a0b:df::4005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225668/index.m3u8
350 | #EXTINF:-1 tvg-id="N02" tvg-name="NewTV军旅剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV02.png" group-title="NewTV",NewTV军旅剧场
351 | http://[2409:8087:1a0b:df::4018]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225676/index.m3u8
352 | #EXTINF:-1 tvg-id="N05" tvg-name="NewTV家庭剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV05.png" group-title="NewTV",NewTV家庭剧场
353 | http://[2409:8087:1a0b:df::4007]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225677/index.m3u8
354 | #EXTINF:-1 tvg-id="N07" tvg-name="NewTV中国功夫" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV07.png" group-title="NewTV",NewTV中国功夫
355 | http://[2409:8087:1a0b:df::4001]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225681/index.m3u8
356 | #EXTINF:-1 tvg-id="N09" tvg-name="NewTV东北热剧" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV09.png" group-title="NewTV",NewTV东北热剧
357 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225741/index.m3u8
358 | #EXTINF:-1 tvg-id="N10" tvg-name="NewTV惊悚悬疑" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV10.png" group-title="NewTV",NewTV惊悚悬疑
359 | http://[2409:8087:1a0b:df::4002]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225665/index.m3u8
360 | #EXTINF:-1 tvg-id="N12" tvg-name="NewTV明星大片" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV12.png" group-title="NewTV",NewTV明星大片
361 | http://[2409:8087:1a0b:df::4005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225664/index.m3u8
362 | #EXTINF:-1 tvg-id="N13" tvg-name="NewTV欢乐剧场" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV13.png" group-title="NewTV",NewTV欢乐剧场
363 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225742/index.m3u8
364 | #EXTINF:-1 tvg-id="N15" tvg-name="NewTV潮妈辣婆" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV15.png" group-title="NewTV",NewTV潮妈辣婆
365 | http://[2409:8087:1a0b:df::4007]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225685/index.m3u8
366 | #EXTINF:-1 tvg-id="N16" tvg-name="NewTV炫舞未来" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV16.png" group-title="NewTV",NewTV炫舞未来
367 | http://[2409:8087:1a0b:df::4004]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225674/index.m3u8
368 | #EXTINF:-1 tvg-id="N18" tvg-name="NewTV精品体育" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV18.png" group-title="NewTV",NewTV精品体育
369 | http://[2409:8087:1a0b:df::4004]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225674/index.m3u8
370 | #EXTINF:-1 tvg-id="N19" tvg-name="NewTV精品大剧" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV19.png" group-title="NewTV",NewTV精品大剧
371 | http://[2409:8087:1a0b:df::4001]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225670/index.m3u8
372 | #EXTINF:-1 tvg-id="N23" tvg-name="NewTV超级电影" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV23.png" group-title="NewTV",NewTV超级电影
373 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225717/index.m3u8
374 | #EXTINF:-1 tvg-id="N24" tvg-name="NewTV超级综艺" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV24.png" group-title="NewTV",NewTV超级综艺
375 | http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225714/index.m3u8
376 | #EXTINF:-1 tvg-id="N25" tvg-name="NewTV超级电视剧" tvg-logo="https://cdn.jsdelivr.net/gh/wanglindl/TVlogo@main/img/NewTV25.png" group-title="NewTV",NewTV超级电视剧
377 | http://[2409:8087:1a01:df::7005]:80/ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225716/index.m3u8
378 |
379 |
380 | #咪咕体育/34个频道
381 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播4K-1" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播4K-1
382 | http://gslbserv.itv.cmvideo.cn:80/3000000010000005180/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000005180&livemode=1&stbId=3
383 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播4K-2" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播4K-2
384 | http://gslbserv.itv.cmvideo.cn:80/3000000010000015686/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000015686&livemode=1&stbId=3
385 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-1" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-1
386 | http://gslbserv.itv.cmvideo.cn:80/3000000001000005308/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000005308&livemode=1&stbId=3
387 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-2" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-2
388 | http://gslbserv.itv.cmvideo.cn:80/3000000001000005969/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000005969&livemode=1&stbId=3
389 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-3" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-3
390 | http://gslbserv.itv.cmvideo.cn:80/3000000001000007218/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000007218&livemode=1&stbId=3
391 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-4" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-4
392 | http://gslbserv.itv.cmvideo.cn:80/3000000001000008001/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000008001&livemode=1&stbId=3
393 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-5" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-5
394 | http://gslbserv.itv.cmvideo.cn:80/3000000001000008176/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000008176&livemode=1&stbId=3
395 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-6" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-6
396 | http://gslbserv.itv.cmvideo.cn:80/3000000001000008379/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000008379&livemode=1&stbId=3
397 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-7" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-7
398 | http://gslbserv.itv.cmvideo.cn:80/3000000001000010129/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000010129&livemode=1&stbId=3
399 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-8" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-8
400 | http://gslbserv.itv.cmvideo.cn:80/3000000001000010948/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000010948&livemode=1&stbId=3
401 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-9" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-9
402 | http://gslbserv.itv.cmvideo.cn:80/3000000001000028638/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000028638&livemode=1&stbId=3
403 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-10" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-10
404 | http://gslbserv.itv.cmvideo.cn:80/3000000001000031494/index.m3u8?channel-id=FifastbLive&Contentid=3000000001000031494&livemode=1&stbId=3
405 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-11" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-11
406 | http://gslbserv.itv.cmvideo.cn:80/3000000010000000097/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000000097&livemode=1&stbId=3
407 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-12" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-12
408 | http://gslbserv.itv.cmvideo.cn:80/3000000010000002019/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000002019&livemode=1&stbId=3
409 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-13" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-13
410 | http://gslbserv.itv.cmvideo.cn:80/3000000010000002809/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000002809&livemode=1&stbId=3
411 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-14" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-14
412 | http://gslbserv.itv.cmvideo.cn:80/3000000010000003915/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000003915&livemode=1&stbId=3
413 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-15" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-15
414 | http://gslbserv.itv.cmvideo.cn:80/3000000010000004193/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000004193&livemode=1&stbId=3
415 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-16" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-16
416 | http://gslbserv.itv.cmvideo.cn:80/3000000010000005837/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000005837&livemode=1&stbId=3
417 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-17" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-17
418 | http://gslbserv.itv.cmvideo.cn:80/3000000010000006077/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000006077&livemode=1&stbId=3
419 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-18" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-18
420 | http://gslbserv.itv.cmvideo.cn:80/3000000010000006658/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000006658&livemode=1&stbId=3
421 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-19" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-19
422 | http://gslbserv.itv.cmvideo.cn:80/3000000010000009788/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000009788&livemode=1&stbId=3
423 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-20" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-20
424 | http://gslbserv.itv.cmvideo.cn:80/3000000010000010833/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000010833&livemode=1&stbId=3
425 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-21" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-21
426 | http://gslbserv.itv.cmvideo.cn:80/3000000010000011297/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000011297&livemode=1&stbId=3
427 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-22" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-22
428 | http://gslbserv.itv.cmvideo.cn:80/3000000010000011518/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000011518&livemode=1&stbId=3
429 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-23" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-23
430 | http://gslbserv.itv.cmvideo.cn:80/3000000010000012558/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000012558&livemode=1&stbId=3
431 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-24" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-24
432 | http://gslbserv.itv.cmvideo.cn:80/3000000010000012616/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000012616&livemode=1&stbId=3
433 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-25" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-25
434 | http://gslbserv.itv.cmvideo.cn:80/3000000010000015470/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000015470&livemode=1&stbId=3
435 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-26" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-26
436 | http://gslbserv.itv.cmvideo.cn:80/3000000010000015560/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000015560&livemode=1&stbId=3
437 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-27" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-27
438 | http://gslbserv.itv.cmvideo.cn:80/3000000010000019839/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000019839&livemode=1&stbId=3
439 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-28" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-28
440 | http://gslbserv.itv.cmvideo.cn:80/3000000010000021904/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000021904&livemode=1&stbId=3
441 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-29" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-29
442 | http://gslbserv.itv.cmvideo.cn:80/3000000010000023434/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000023434&livemode=1&stbId=3
443 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-30" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-30
444 | http://gslbserv.itv.cmvideo.cn:80/3000000010000025380/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000025380&livemode=1&stbId=3
445 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-31" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-31
446 | http://gslbserv.itv.cmvideo.cn:80/3000000010000027691/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000027691&livemode=1&stbId=3
447 | #EXTINF:-1 tvg-id="migu" tvg-name="咪咕直播-32" tvg-logo="https://cdn.jsdelivr.net/gh/Meroser/IPTV@main/TVlogo/MG.png" group-title="移动咪咕",咪咕直播-32
448 | http://gslbserv.itv.cmvideo.cn:80/3000000010000031669/index.m3u8?channel-id=FifastbLive&Contentid=3000000010000031669&livemode=1&stbId=3
449 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "SimpleTV"
17 | include(":app")
18 |
--------------------------------------------------------------------------------