mirror of
https://github.com/lizongying/my-tv.git
synced 2026-10-29 15:46:36 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
709cc2ccdf | ||
|
|
3b01debf7f | ||
|
|
20ddba6ad7 | ||
|
|
c524e6c0fb | ||
|
|
2bd9841971 | ||
|
|
dfe446e6e1 | ||
|
|
03a6efe179 | ||
|
|
613615d582 | ||
|
|
ba1f58eb4f | ||
|
|
6d722220ae | ||
|
|
27607d23c8 |
+14
-19
@@ -5,18 +5,11 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write # to deploy to Pages
|
|
||||||
id-token: write # to verify the deployment originates from an appropriate source
|
|
||||||
|
|
||||||
# Deploy to the github-pages environment
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{steps.deployment.outputs.page_url}}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -27,17 +20,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
cache: 'gradle'
|
|
||||||
|
|
||||||
- name: Sign artifact
|
- name: Run build with Gradle wrapper
|
||||||
id: signArtifact
|
run: ./gradlew assembleRelease
|
||||||
uses: r0adkll/sign-android-release@v1.0.1
|
|
||||||
|
- name: Sign app APK
|
||||||
|
id: sign_app
|
||||||
|
uses: r0adkll/sign-android-release@v1
|
||||||
with:
|
with:
|
||||||
releaseDirectory: app/build/outputs/apk/ourbuildtype/release
|
releaseDirectory: app/build/outputs/apk/release
|
||||||
alias: ${{ secrets.KEYALIAS }}
|
alias: ${{ secrets.ALIAS }}
|
||||||
signingKeyBase64: ${{ secrets.KEYSTORE }}
|
signingKeyBase64: ${{ secrets.KEYSTORE }}
|
||||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
keyPassword: ${{ secrets.ALIAS_PASSWORD }}
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
@@ -56,6 +51,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./app/build/outputs/apk/ourbuildtype/release/app-artifact-*.apk
|
asset_path: ${{ steps.sign_app.outputs.signedReleaseFile }}
|
||||||
asset_name: my_tv.apk
|
asset_name: my-tv-{{ github.ref_name }}.apk
|
||||||
asset_content_type: application/vnd.android.package-archive
|
asset_content_type: application/vnd.android.package-archive
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 我的电视
|
||||||
|
|
||||||
|
安卓电视直播软件,内置直播源
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
下载安装[releases](https://github.com/lizongying/my-tv/releases)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## 其他
|
||||||
|
|
||||||
|
小米电视可以使用小米电视助手进行安装
|
||||||
|
|
||||||
|
如电视可以启用ADB,也可以通过ADB进行安装:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
adb install my-tv.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
## 赞赏
|
||||||
|
|
||||||
|

|
||||||
+22
-4
@@ -11,9 +11,8 @@ android {
|
|||||||
applicationId "com.lizongying.mytv"
|
applicationId "com.lizongying.mytv"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 1
|
versionCode VersionCode()
|
||||||
versionName "1.0"
|
versionName VersionName()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -33,8 +32,27 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
static def VersionCode() {
|
||||||
|
try {
|
||||||
|
def process = 'git rev-list --count HEAD'.execute()
|
||||||
|
process.waitFor()
|
||||||
|
return process.text.toInteger()
|
||||||
|
} catch (ignored) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static def VersionName() {
|
||||||
|
try {
|
||||||
|
def process = 'git describe --tags --always'.execute()
|
||||||
|
process.waitFor()
|
||||||
|
return process.text.trim() - "v"
|
||||||
|
} catch (ignored) {
|
||||||
|
return "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
implementation 'androidx.core:core-ktx:1.11.0-beta02'
|
implementation 'androidx.core:core-ktx:1.11.0-beta02'
|
||||||
implementation 'androidx.leanback:leanback:1.0.0'
|
implementation 'androidx.leanback:leanback:1.0.0'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
<uses-feature
|
||||||
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.software.leanback"
|
||||||
|
android:required="true" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@@ -13,24 +18,13 @@
|
|||||||
android:banner="@drawable/tv"
|
android:banner="@drawable/tv"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@drawable/tv"
|
android:icon="@drawable/tv"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:logo="@drawable/tv"
|
android:logo="@drawable/tv"
|
||||||
android:screenOrientation="landscape">
|
android:screenOrientation="landscape">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.software.leanback"
|
|
||||||
android:required="true" />
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.touchscreen"
|
|
||||||
android:required="false" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -2,7 +2,6 @@ package com.lizongying.mytv
|
|||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.util.Log
|
|
||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.leanback.widget.ImageCardView
|
import androidx.leanback.widget.ImageCardView
|
||||||
@@ -13,16 +12,9 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A CardPresenter is used to generate Views and bind Objects to them on demand.
|
|
||||||
* It contains an ImageCardView.
|
|
||||||
*/
|
|
||||||
class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Presenter() {
|
class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Presenter() {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
||||||
Log.d(TAG, "onCreateViewHolder")
|
|
||||||
|
|
||||||
val cardView = object :
|
val cardView = object :
|
||||||
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {
|
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {
|
||||||
override fun setSelected(selected: Boolean) {
|
override fun setSelected(selected: Boolean) {
|
||||||
@@ -40,11 +32,9 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese
|
|||||||
val tv = item as TV
|
val tv = item as TV
|
||||||
val cardView = viewHolder.view as ImageCardView
|
val cardView = viewHolder.view as ImageCardView
|
||||||
|
|
||||||
Log.d(TAG, "onBindViewHolder")
|
cardView.titleText = tv.title
|
||||||
if (tv.videoUrl != null) {
|
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
|
||||||
cardView.titleText = tv.title
|
cardView.tag = tv.videoUrl
|
||||||
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
|
|
||||||
cardView.tag = tv.videoUrl
|
|
||||||
|
|
||||||
// lifecycleScope.launch(Dispatchers.IO) {
|
// lifecycleScope.launch(Dispatchers.IO) {
|
||||||
// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) }
|
// val videoThumbnail = tv.videoUrl?.let { getVideoThumbnail(it) }
|
||||||
@@ -53,11 +43,9 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese
|
|||||||
// cardView.mainImageView.setImageBitmap(videoThumbnail)
|
// cardView.mainImageView.setImageBitmap(videoThumbnail)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
|
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
|
||||||
Log.d(TAG, "onUnbindViewHolder")
|
|
||||||
val cardView = viewHolder.view as ImageCardView
|
val cardView = viewHolder.view as ImageCardView
|
||||||
// Remove references to images so that the garbage collector can free up memory
|
// Remove references to images so that the garbage collector can free up memory
|
||||||
cardView.badgeImage = null
|
cardView.badgeImage = null
|
||||||
@@ -95,9 +83,7 @@ class CardPresenter(private val lifecycleScope: LifecycleCoroutineScope) : Prese
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "CardPresenter"
|
private const val TAG = "CardPresenter"
|
||||||
|
|
||||||
private const val CARD_WIDTH = 313
|
private const val CARD_WIDTH = 313
|
||||||
|
|
||||||
private const val CARD_HEIGHT = 176
|
private const val CARD_HEIGHT = 176
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.lizongying.mytv
|
|||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
|
|
||||||
data class Info(
|
data class Info(
|
||||||
var rowPosition: Int = 0,
|
var rowPosition: Int = 0,
|
||||||
var itemPosition: Int = 0,
|
var itemPosition: Int = 0,
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ import android.widget.Toast
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads [MainFragment].
|
|
||||||
*/
|
|
||||||
class MainActivity : FragmentActivity() {
|
class MainActivity : FragmentActivity() {
|
||||||
|
|
||||||
private val playbackFragment = PlaybackFragment()
|
private val playbackFragment = PlaybackFragment()
|
||||||
@@ -47,6 +44,14 @@ class MainActivity : FragmentActivity() {
|
|||||||
mainFragment.next()
|
mainFragment.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun prevSource() {
|
||||||
|
mainFragment.prevSource()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextSource() {
|
||||||
|
mainFragment.nextSource()
|
||||||
|
}
|
||||||
|
|
||||||
fun switchMainFragment() {
|
fun switchMainFragment() {
|
||||||
val transaction = supportFragmentManager.beginTransaction()
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
|
|
||||||
@@ -60,15 +65,15 @@ class MainActivity : FragmentActivity() {
|
|||||||
transaction.commit()
|
transaction.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun focusMainFragment() {
|
private fun focusMainFragment() {
|
||||||
mainFragment.focus()
|
mainFragment.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fragmentIsHidden(): Boolean {
|
fun mainFragmentIsHidden(): Boolean {
|
||||||
return mainFragment.isHidden
|
return mainFragment.isHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideMainFragment() {
|
private fun hideMainFragment() {
|
||||||
if (!mainFragment.isHidden) {
|
if (!mainFragment.isHidden) {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.hide(mainFragment)
|
.hide(mainFragment)
|
||||||
@@ -79,12 +84,16 @@ class MainActivity : FragmentActivity() {
|
|||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
when (keyCode) {
|
when (keyCode) {
|
||||||
KeyEvent.KEYCODE_BACK -> {
|
KeyEvent.KEYCODE_BACK -> {
|
||||||
|
if (!mainFragmentIsHidden()) {
|
||||||
|
hideMainFragment()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if (doubleBackToExitPressedOnce) {
|
if (doubleBackToExitPressedOnce) {
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
hideMainFragment()
|
|
||||||
this.doubleBackToExitPressedOnce = true
|
this.doubleBackToExitPressedOnce = true
|
||||||
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
@@ -112,9 +121,14 @@ class MainActivity : FragmentActivity() {
|
|||||||
layoutParams.gravity = Gravity.BOTTOM
|
layoutParams.gravity = Gravity.BOTTOM
|
||||||
imageView.layoutParams = layoutParams
|
imageView.layoutParams = layoutParams
|
||||||
|
|
||||||
|
|
||||||
|
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
|
|
||||||
|
val versionName = packageInfo.versionName
|
||||||
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||||
builder
|
builder
|
||||||
.setTitle("https://github.com/lizongying/my-tv/releases")
|
.setTitle("当前版本: $versionName, 获取最新: https://github.com/lizongying/my-tv/releases/")
|
||||||
.setView(linearLayout)
|
.setView(linearLayout)
|
||||||
|
|
||||||
val dialog: AlertDialog = builder.create()
|
val dialog: AlertDialog = builder.create()
|
||||||
@@ -140,13 +154,13 @@ class MainActivity : FragmentActivity() {
|
|||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||||
if (mainFragment.isHidden) {
|
if (mainFragment.isHidden) {
|
||||||
prev()
|
prevSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||||
if (mainFragment.isHidden) {
|
if (mainFragment.isHidden) {
|
||||||
next()
|
nextSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package com.lizongying.mytv
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.leanback.app.BrowseSupportFragment
|
import androidx.leanback.app.BrowseSupportFragment
|
||||||
import androidx.leanback.widget.ArrayObjectAdapter
|
import androidx.leanback.widget.ArrayObjectAdapter
|
||||||
import androidx.leanback.widget.HeaderItem
|
import androidx.leanback.widget.HeaderItem
|
||||||
@@ -18,9 +16,6 @@ import androidx.leanback.widget.Row
|
|||||||
import androidx.leanback.widget.RowPresenter
|
import androidx.leanback.widget.RowPresenter
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a grid of cards with movies to browse.
|
|
||||||
*/
|
|
||||||
class MainFragment : BrowseSupportFragment() {
|
class MainFragment : BrowseSupportFragment() {
|
||||||
|
|
||||||
var itemPosition: Int = 0
|
var itemPosition: Int = 0
|
||||||
@@ -32,24 +27,17 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
||||||
setupUIElements()
|
setupUIElements()
|
||||||
|
|
||||||
loadRows()
|
loadRows()
|
||||||
|
|
||||||
setupEventListeners()
|
setupEventListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show() {
|
|
||||||
if (!view?.isVisible!!) {
|
|
||||||
view?.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupUIElements() {
|
private fun setupUIElements() {
|
||||||
// set fastLane (or headers) background color
|
|
||||||
brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background)
|
brandColor = ContextCompat.getColor(context!!, R.color.fastlane_background)
|
||||||
// headersState = HEADERS_DISABLED
|
// headersState = HEADERS_DISABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var count: Int = 0
|
||||||
|
|
||||||
private fun loadRows() {
|
private fun loadRows() {
|
||||||
val list = TVList.list
|
val list = TVList.list
|
||||||
val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
|
val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
|
||||||
@@ -58,9 +46,20 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
var idx: Long = 0
|
var idx: Long = 0
|
||||||
for ((k, v) in list) {
|
for ((k, v) in list) {
|
||||||
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
|
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
|
||||||
for ((idx2, v1) in v.withIndex()) {
|
var idx2 = 0
|
||||||
|
for ((k1, v1) in v) {
|
||||||
listRowAdapter.add(v1)
|
listRowAdapter.add(v1)
|
||||||
list2.add(Info(idx.toInt(), idx2, v1))
|
list2.add(
|
||||||
|
Info(
|
||||||
|
idx.toInt(), idx2, TV(
|
||||||
|
count,
|
||||||
|
k1,
|
||||||
|
v1.toList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
count++
|
||||||
|
idx2++
|
||||||
}
|
}
|
||||||
val header = HeaderItem(idx, k)
|
val header = HeaderItem(idx, k)
|
||||||
rowsAdapter.add(ListRow(header, listRowAdapter))
|
rowsAdapter.add(ListRow(header, listRowAdapter))
|
||||||
@@ -69,7 +68,7 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
|
|
||||||
adapter = rowsAdapter
|
adapter = rowsAdapter
|
||||||
|
|
||||||
(activity as? MainActivity)?.play(list.values.first()[0])
|
(activity as? MainActivity)?.play(list2.first().item as TV)
|
||||||
(activity as? MainActivity)?.switchMainFragment()
|
(activity as? MainActivity)?.switchMainFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
)
|
)
|
||||||
// Toast.makeText(
|
// Toast.makeText(
|
||||||
// activity,
|
// activity,
|
||||||
// "${l.title} $selectedPosition $itemPosition",
|
// "${l.title} ${tv.videoIndex}",
|
||||||
// Toast.LENGTH_SHORT
|
// Toast.LENGTH_SHORT
|
||||||
// ).show()
|
// ).show()
|
||||||
}
|
}
|
||||||
@@ -115,7 +114,45 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
)
|
)
|
||||||
// Toast.makeText(
|
// Toast.makeText(
|
||||||
// activity,
|
// activity,
|
||||||
// "${l.title} $selectedPosition $itemPosition",
|
// "${l.title} ${tv.videoIndex}",
|
||||||
|
// Toast.LENGTH_SHORT
|
||||||
|
// ).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun prevSource() {
|
||||||
|
view?.post {
|
||||||
|
val item = list2[itemPosition]
|
||||||
|
val tv = item.item as TV
|
||||||
|
|
||||||
|
tv.videoIndex--
|
||||||
|
if (tv.videoIndex == -1) {
|
||||||
|
tv.videoIndex = tv.videoUrl.size - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
(activity as? MainActivity)?.play(tv)
|
||||||
|
// Toast.makeText(
|
||||||
|
// activity,
|
||||||
|
// "${l.title} ${tv.videoIndex}",
|
||||||
|
// Toast.LENGTH_SHORT
|
||||||
|
// ).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextSource() {
|
||||||
|
view?.post {
|
||||||
|
val item = list2[itemPosition]
|
||||||
|
val tv = item.item as TV
|
||||||
|
|
||||||
|
tv.videoIndex++
|
||||||
|
if (tv.videoIndex == tv.videoUrl.size) {
|
||||||
|
tv.videoIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
(activity as? MainActivity)?.play(tv)
|
||||||
|
// Toast.makeText(
|
||||||
|
// activity,
|
||||||
|
// "${l.title} ${tv.videoIndex}",
|
||||||
// Toast.LENGTH_SHORT
|
// Toast.LENGTH_SHORT
|
||||||
// ).show()
|
// ).show()
|
||||||
}
|
}
|
||||||
@@ -150,7 +187,6 @@ class MainFragment : BrowseSupportFragment() {
|
|||||||
) {
|
) {
|
||||||
if (item is TV) {
|
if (item is TV) {
|
||||||
Log.i(TAG, "Item: ${item.id}")
|
Log.i(TAG, "Item: ${item.id}")
|
||||||
|
|
||||||
}
|
}
|
||||||
if (itemViewHolder == null) {
|
if (itemViewHolder == null) {
|
||||||
view?.post {
|
view?.post {
|
||||||
|
|||||||
@@ -20,30 +20,31 @@ class PlaybackControlGlue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
|
||||||
(context as? MainActivity)?.prev()
|
(context as? MainActivity)?.prev()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
|
||||||
(context as? MainActivity)?.next()
|
(context as? MainActivity)?.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
|
||||||
(context as? MainActivity)?.prev()
|
(context as? MainActivity)?.prevSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||||
if ((context as? MainActivity)?.fragmentIsHidden() == true) {
|
if ((context as? MainActivity)?.mainFragmentIsHidden() == true) {
|
||||||
(context as? MainActivity)?.next()
|
(context as? MainActivity)?.nextSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onKey(v, keyCode, event)
|
return super.onKey(v, keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.leanback.media.PlaybackTransportControlGlue
|
|||||||
import androidx.leanback.widget.PlaybackControlsRow
|
import androidx.leanback.widget.PlaybackControlsRow
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
/** Handles video playback with media controls. */
|
|
||||||
class PlaybackFragment : VideoSupportFragment() {
|
class PlaybackFragment : VideoSupportFragment() {
|
||||||
|
|
||||||
private lateinit var mTransportControlGlue: PlaybackTransportControlGlue<MediaPlayerAdapter>
|
private lateinit var mTransportControlGlue: PlaybackTransportControlGlue<MediaPlayerAdapter>
|
||||||
@@ -28,21 +27,17 @@ class PlaybackFragment : VideoSupportFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showControlsOverlay(runAnimation: Boolean) {
|
override fun showControlsOverlay(runAnimation: Boolean) {
|
||||||
// We will do nothing here, and thus controls will never be shown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(tv: TV) {
|
fun play(tv: TV) {
|
||||||
if (tv.videoUrl.isNullOrBlank()) {
|
val videoUrl = tv.videoUrl[tv.videoIndex]
|
||||||
Log.e(TAG, "videoUrl is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tv.videoUrl.equals(lastVideoUrl)) {
|
if (videoUrl == lastVideoUrl) {
|
||||||
Log.e(TAG, "videoUrl is duplication")
|
Log.e(TAG, "videoUrl is duplication")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastVideoUrl = tv.videoUrl!!
|
lastVideoUrl = videoUrl
|
||||||
|
|
||||||
playerAdapter?.reset()
|
playerAdapter?.reset()
|
||||||
|
|
||||||
@@ -52,9 +47,8 @@ class PlaybackFragment : VideoSupportFragment() {
|
|||||||
mTransportControlGlue.playWhenPrepared()
|
mTransportControlGlue.playWhenPrepared()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
playerAdapter?.setDataSource(Uri.parse(tv.videoUrl))
|
playerAdapter?.setDataSource(Uri.parse(videoUrl))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
// Handle the exception
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hideControlsOverlay(false)
|
hideControlsOverlay(false)
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ package com.lizongying.mytv
|
|||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
/**
|
|
||||||
* Movie class represents video entity with title, description, image thumbs and video url.
|
|
||||||
*/
|
|
||||||
data class TV(
|
data class TV(
|
||||||
var id: Int = 0,
|
var id: Int = 0,
|
||||||
var title: String? = null,
|
var title: String,
|
||||||
var videoUrl: String? = null,
|
var videoUrl: List<String>,
|
||||||
|
var videoIndex: Int = 0,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@@ -16,6 +14,7 @@ data class TV(
|
|||||||
"id=" + id +
|
"id=" + id +
|
||||||
", title='" + title + '\'' +
|
", title='" + title + '\'' +
|
||||||
", videoUrl='" + videoUrl + '\'' +
|
", videoUrl='" + videoUrl + '\'' +
|
||||||
|
", videoIndex='" + videoIndex + '\'' +
|
||||||
'}'
|
'}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,115 +1,125 @@
|
|||||||
package com.lizongying.mytv
|
package com.lizongying.mytv
|
||||||
|
|
||||||
object TVList {
|
object TVList {
|
||||||
val list: Map<String, List<TV>> by lazy {
|
val list: Map<String, Map<String, Set<String>>> by lazy {
|
||||||
setupTV()
|
setupTV()
|
||||||
}
|
}
|
||||||
private var count: Int = 0
|
|
||||||
|
|
||||||
private fun setupTV(): Map<String, List<TV>> {
|
private fun setupTV(): Map<String, Map<String, Set<String>>> {
|
||||||
val tv = arrayOf(
|
val tvs = """
|
||||||
arrayOf(
|
央视频道,CCTV1,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=
|
||||||
"央视频道",
|
央视频道,CCTV2,https://iptv.luas.edu.cn/liverespath/6b13fe5368d391761312a985ace065c0ecad2f5e/877097d2fa-0-0-b7736e6941fd5cb71f45ef9397b68092/index.m3u8
|
||||||
"CCTV1",
|
央视频道,CCTV2,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=",
|
央视频道,CCTV3,https://iptv.luas.edu.cn/liverespath/f76f9947c68be18d7a456e25aa59a08c5747e6a5/0df24da9ec-0-0-dca40ddadd2a051ce1a83536d9310820/index.m3u8
|
||||||
),
|
央视频道,CCTV3,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=
|
||||||
arrayOf(
|
央视频道,CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8
|
||||||
"央视频道",
|
央视频道,CCTV4 中文国际,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=
|
||||||
"CCTV2",
|
央视频道,CCTV4 美洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=",
|
央视频道,CCTV4 欧洲,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=
|
||||||
),
|
央视频道,CCTV5,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=
|
||||||
arrayOf(
|
央视频道,CCTV5+,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=
|
||||||
"央视频道",
|
央视频道,CCTV6,https://iptv.luas.edu.cn/liverespath/f2f39ee2105c85c32df375728a51b5d89d3afab4/113ed89f48-0-0-85216e55861329ec31ba1437a2ff37c9/index.m3u8
|
||||||
"CCTV3",
|
央视频道,CCTV6,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv6hd/3000/index.m3u8?&encrypt=
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=",
|
央视频道,CCTV7,https://iptv.luas.edu.cn/liverespath/f116a0a5035935a3435155998163d8eaa60554c3/4ba629f762-0-0-0a66b18805ff859ce68ab1137157079e/index.m3u8
|
||||||
),
|
央视频道,CCTV7,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv7hd/51/index.m3u8?&encrypt=
|
||||||
arrayOf(
|
央视频道,CCTV8,https://iptv.luas.edu.cn/liverespath/9e6e3b618b5dc902d992949f0c669bb674f6cde8/cae4471c68-0-0-316245c664c3311072c7279ec29672fe/index.m3u8
|
||||||
"央视频道",
|
央视频道,CCTV8,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv8hd/3000/index.m3u8?&encrypt=
|
||||||
"CCTV4",
|
央视频道,CCTV9,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=",
|
央视频道,CCTV9,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv9hd/57/index.m3u8?&encrypt=
|
||||||
),
|
央视频道,CCTV10,https://iptv.luas.edu.cn/liverespath/924e6292a0e6f7440e8380075908fed9915b3c00/780499a193-0-0-c50d57fd93cf32a5144ec8d889eb3ed9/index.m3u8
|
||||||
arrayOf(
|
央视频道,CCTV10,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/cctv10hd/2000/index.m3u8?&encrypt=
|
||||||
"央视频道",
|
央视频道,CCTV11,https://iptv.luas.edu.cn/liverespath/3d0aa0f1604f13f0ae90c2dc0590ca22a1bcaaa2/646e868153-0-0-a53121a1df5b93a74d1980270e652878/index.m3u8
|
||||||
"CCTV4美洲",
|
央视频道,CCTV11,http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv11hd/57/20200103/index.m3u8?&encrypt=
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=",
|
央视频道,CCTV12,https://iptv.luas.edu.cn/liverespath/fb84bde1de15cdb0308a7910cebc0497594ae94e/e2d8885f42-0-0-6e1f55986652ba786606a116c5cc0775/index.m3u8
|
||||||
),
|
央视频道,CCTV12,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv12hd/57/index.m3u8?&encrypt=
|
||||||
arrayOf(
|
央视频道,CCTV13,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8
|
||||||
"央视频道",
|
央视频道,CCTV13,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctv13/2000/index.m3u8?&encrypt=
|
||||||
"CCTV4欧洲",
|
央视频道,CCTV14,https://iptv.luas.edu.cn/liverespath/0e0973e58d4835f4b872548164462930003f77b4/0ef01acfdb-0-0-3c10e95bcde07ba14f0f93a8d831b684/index.m3u8
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=",
|
央视频道,CCTV14,http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv14hd/3000/index.m3u8?&encrypt=
|
||||||
),
|
央视频道,CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8
|
||||||
arrayOf(
|
央视频道,CCTV15,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv15hd/51/index.m3u8?&encrypt=
|
||||||
"央视频道",
|
央视频道,CCTV17,http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv17hd/57/index.m3u8?&encrypt=
|
||||||
"CCTV5",
|
央视频道,CGTN,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/cctvnews/1000/index.m3u8?&encrypt=
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=",
|
央视频道,CGTN 新闻频道,http://live.cgtn.com/1000/prog_index.m3u8
|
||||||
),
|
央视频道,CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
|
||||||
arrayOf(
|
央视频道,CGTN 法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8
|
||||||
"央视频道",
|
央视频道,CGTN 俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8
|
||||||
"CCTV5+",
|
央视频道,CGTN 西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=",
|
央视频道,CGTN 阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8
|
||||||
),
|
央视频道,CGTN 拉丁美洲频道,http://livees.cgtn.com/1000e/prog_index.m3u8
|
||||||
arrayOf(
|
央视频道,书画频道,http://211.103.180.178:8234/live_hls/hdmi.m3u8
|
||||||
"央视频",
|
|
||||||
"CCTV1",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv1hd265/57/20191230/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV2",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv2hd265/55/20200407/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV3",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/ocn/cctv3hd/3000/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV4",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv4hd/1500/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV4美洲",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4meihd/57/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV4欧洲",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/20200324/cctv4ouhd/51/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV5",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/migu/kailu/cctv5hd265/57/20191230/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
arrayOf(
|
|
||||||
"央视频",
|
|
||||||
"CCTV5+",
|
|
||||||
"http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/cctv5plusnew/2500/index.m3u8?&encrypt=",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val map: MutableMap<String, MutableList<TV>> = mutableMapOf()
|
地方频道,东方卫视,http://hlsbkmgsplive.miguvideo.com/wd_r4/dfl/dongfangwshd/3000/index.m3u8?&encrypt=
|
||||||
|
地方频道,内蒙古卫视,http://hlsbkmgsplive.miguvideo.com/envivo_w/2018/SD/neimeng/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,湖南卫视,http://hlsbkmgsplive.miguvideo.com/wd-hunanhd-2500/index.m3u8?&encrypt=
|
||||||
|
地方频道,湖北卫视,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/hubeiwshd/2000/index.m3u8?&encrypt=
|
||||||
|
地方频道,辽宁卫视,http://hlsbkmgsplive.miguvideo.com/wd_r2/2018/ocn/liaoningwshd/2000/index.m3u8?&encrypt=
|
||||||
|
地方频道,江苏卫视,http://hlsbkmgsplive.miguvideo.com/wd_r2/cctv/jiangsuhd/2500/index.m3u8?&encrypt=
|
||||||
|
地方频道,江西卫视,http://hlsbkmgsplive.miguvideo.com/migu/kailu/jxwshd/57/20190820/index.m3u8?&encrypt=
|
||||||
|
地方频道,西藏卫视,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/xizang/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,山东卫视,http://hlsbkmgsplive.miguvideo.com/wd-shandonghd-1200/index.m3u8?&encrypt=
|
||||||
|
地方频道,广东卫视,http://hlsbkmgsplive.miguvideo.com/ws_w/2018/gdws/gdws2000/2000/index.m3u8?&encrypt=
|
||||||
|
地方频道,广西卫视,http://hlsbkmgsplive.miguvideo.com/wd-guangxiwssd-600/index.m3u8?&encrypt=
|
||||||
|
地方频道,重庆卫视,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/chongqing/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,甘肃卫视,http://hlsbkmgsplive.miguvideo.com/envivo_v/2018/SD/gansu/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,青海卫视,http://hlsbkmgsplive.miguvideo.com/envivo_w/2018/SD/qinghai/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,陕西卫视,http://hlsbkmgsplive.miguvideo.com/envivo_w/2018/SD/shan3xi/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,河南卫视,http://hlsbkmgsplive.miguvideo.com/wd-henanwssd-600/index.m3u8?&encrypt=
|
||||||
|
地方频道,大湾区卫视,http://hlsbkmgsplive.miguvideo.com/wd_r3/2018/nfmedia/nfws/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,云南卫视,http://hlsbkmgsplive.miguvideo.com/envivo_x/2018/SD/yunnan/1000/index.m3u8?&encrypt=
|
||||||
|
地方频道,贵州卫视,http://hlsbkmgsplive.miguvideo.com/wd-guizhouwssd-600/index.m3u8?&encrypt=
|
||||||
|
地方频道,新疆卫视,http://hlsbkmgsplive.miguvideo.com/wd-xinjiangwssd-600/index.m3u8?&encrypt=
|
||||||
|
地方频道,北京卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225728/index.m3u8
|
||||||
|
地方频道,重庆卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226409/index.m3u8
|
||||||
|
地方频道,天津卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225740/index.m3u8
|
||||||
|
地方频道,黑龙江卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226327/index.m3u8
|
||||||
|
地方频道,辽宁卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8
|
||||||
|
地方频道,吉林卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226397/index.m3u8
|
||||||
|
地方频道,河北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226406/index.m3u8
|
||||||
|
地方频道,河南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226480/index.m3u8
|
||||||
|
地方频道,山东卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226209/index.m3u8
|
||||||
|
地方频道,山西卫视,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225763/index.m3u8
|
||||||
|
地方频道,湖北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8
|
||||||
|
地方频道,湖南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8
|
||||||
|
地方频道,浙江卫视,http://hw-m-l.cztv.com/channels/lantian/channel01/1080p.m3u8
|
||||||
|
地方频道,江苏卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8
|
||||||
|
地方频道,江西卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226344/index.m3u8
|
||||||
|
地方频道,安徽卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8
|
||||||
|
地方频道,广东卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226216/index.m3u8
|
||||||
|
地方频道,广西卫视,http://live.gxrb.com.cn/tv/gxtvlive03/index.m3u8
|
||||||
|
地方频道,深圳卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8
|
||||||
|
地方频道,四川卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8
|
||||||
|
地方频道,贵州卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226474/index.m3u8
|
||||||
|
地方频道,云南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226444/index.m3u8
|
||||||
|
地方频道,东南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8
|
||||||
|
地方频道,海南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8
|
||||||
|
地方频道,内蒙古卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226389/index.m3u8
|
||||||
|
地方频道,陕西卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226457/index.m3u8
|
||||||
|
地方频道,甘肃卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225633/index.m3u8
|
||||||
|
地方频道,宁夏卫视,https://hls.ningxiahuangheyun.com/tv/nxws.m3u8
|
||||||
|
地方频道,新疆卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226460/index.m3u8
|
||||||
|
地方频道,青海卫视,http://stream.qhbtv.com/qhws/sd/live.m3u8
|
||||||
|
地方频道,安多卫视,http://stream.qhbtv.com/adws/sd/live.m3u8
|
||||||
|
地方频道,西藏卫视,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226212/index.m3u8
|
||||||
|
地方频道,延边卫视,http://live.ybtvyun.com/video/s10006-44f040627ca1/index.m3u8
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
for (i in tv) {
|
val map: MutableMap<String, MutableMap<String, LinkedHashSet<String>>> = mutableMapOf()
|
||||||
val channelName = i[0]
|
|
||||||
val movieList = map[channelName] ?: mutableListOf()
|
for (i in tvs.split("\n")) {
|
||||||
movieList.add(buildTV(i[1], i[2]))
|
if (!i.contains(",")) {
|
||||||
map[channelName] = movieList
|
continue
|
||||||
|
}
|
||||||
|
val (channel, title, url) = i.split(",")
|
||||||
|
val titleMap = map[channel] ?: mutableMapOf()
|
||||||
|
|
||||||
|
val urlSet = titleMap[title] ?: LinkedHashSet()
|
||||||
|
urlSet.add(url)
|
||||||
|
|
||||||
|
titleMap[title] = urlSet
|
||||||
|
map[channel] = titleMap
|
||||||
}
|
}
|
||||||
|
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildTV(
|
|
||||||
title: String,
|
|
||||||
videoUrl: String,
|
|
||||||
): TV {
|
|
||||||
val tv = TV()
|
|
||||||
tv.id = count++
|
|
||||||
tv.title = title
|
|
||||||
tv.videoUrl = videoUrl
|
|
||||||
return tv
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
Reference in New Issue
Block a user