Files
android-tv/app/src/main/java/com/videoapp/tv/PlayerActivity.kt

211 lines
6.9 KiB
Kotlin
Raw Normal View History

package com.videoapp.tv
import android.os.Bundle
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Button
import android.widget.ImageButton
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import com.videoapp.tv.data.ConfigRepository
import com.videoapp.tv.engine.Episode
import com.videoapp.tv.engine.VideoExtractor
import kotlinx.coroutines.launch
class PlayerActivity : AppCompatActivity() {
private lateinit var playerView: PlayerView
private lateinit var playerWebView: WebView
private lateinit var episodePanel: View
private lateinit var episodeList: android.widget.LinearLayout
private lateinit var loadingIndicator: View
private lateinit var errorText: android.widget.TextView
private lateinit var btnClose: ImageButton
private var exoPlayer: ExoPlayer? = null
private val videoExtractor = VideoExtractor()
private val configRepo by lazy { ConfigRepository(this) }
private var episodes: List<Episode> = emptyList()
private var currentEpisodeIndex = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
playerView = findViewById(R.id.player_view)
playerWebView = findViewById(R.id.player_webview)
episodePanel = findViewById(R.id.episode_panel)
episodeList = findViewById(R.id.episode_list)
loadingIndicator = findViewById(R.id.loading_indicator)
errorText = findViewById(R.id.error_text)
btnClose = findViewById(R.id.btn_close)
val detailUrl = intent.getStringExtra("detail_url") ?: ""
val title = intent.getStringExtra("title") ?: ""
btnClose.setOnClickListener { finish() }
initExoPlayer()
loadVideos(detailUrl, title)
setupTouchListeners()
}
private fun initExoPlayer() {
exoPlayer = ExoPlayer.Builder(this).build().also { player ->
playerView.player = player
}
}
private fun loadVideos(detailUrl: String, @Suppress("UNUSED_PARAMETER") title: String) {
showLoading(true)
val config = configRepo.getConfig()
lifecycleScope.launch {
try {
val videoInfo = videoExtractor.extractVideos(detailUrl, config)
episodes = videoInfo.episodes
if (videoInfo.episodes.isNotEmpty()) {
buildEpisodeUI(videoInfo.episodes)
playEpisode(videoInfo.episodes.first())
} else {
episodePanel.visibility = View.GONE
tryPlayDirectly(detailUrl, config)
}
} catch (e: Exception) {
showError("加载视频失败: ${e.message}")
showLoading(false)
}
}
}
private fun buildEpisodeUI(eps: List<Episode>) {
episodeList.removeAllViews()
episodePanel.visibility = View.VISIBLE
eps.forEachIndexed { index, ep ->
val btn = Button(this).apply {
text = ep.title
setBackgroundResource(R.drawable.episode_selector)
setTextColor(ContextCompat.getColor(this@PlayerActivity, R.color.text_primary))
textSize = 13f
minWidth = 0
setPadding(16, 8, 16, 8)
isFocusable = true
isFocusableInTouchMode = true
setOnClickListener {
currentEpisodeIndex = index
highlightEpisode(it)
playEpisode(ep)
}
}
episodeList.addView(btn)
}
}
private fun playEpisode(ep: Episode) {
showLoading(true)
val config = configRepo.getConfig()
lifecycleScope.launch {
val (directUrl, iframeUrl) = videoExtractor.extractFromPlayPage(ep.playUrl, config)
if (directUrl != null) {
playWithExoPlayer(directUrl)
} else if (iframeUrl != null) {
playWithWebView(iframeUrl)
} else {
playWithWebView(ep.playUrl)
}
}
}
private fun tryPlayDirectly(detailUrl: String, config: com.videoapp.tv.data.SiteConfig) {
lifecycleScope.launch {
val (directUrl, iframeUrl) = videoExtractor.extractFromPlayPage(detailUrl, config)
if (directUrl != null) {
playWithExoPlayer(directUrl)
} else if (iframeUrl != null) {
playWithWebView(iframeUrl)
} else {
playWithWebView(detailUrl)
}
}
}
private fun playWithExoPlayer(url: String) {
playerWebView.visibility = View.GONE
playerView.visibility = View.VISIBLE
showLoading(false)
val mediaItem = MediaItem.fromUri(url)
exoPlayer?.apply {
setMediaItem(mediaItem)
prepare()
playWhenReady = true
}
}
private fun playWithWebView(url: String) {
playerView.visibility = View.GONE
playerWebView.visibility = View.VISIBLE
showLoading(false)
playerWebView.settings.javaScriptEnabled = true
playerWebView.settings.domStorageEnabled = true
playerWebView.settings.mediaPlaybackRequiresUserGesture = false
playerWebView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
showLoading(false)
}
}
playerWebView.loadUrl(url)
}
private fun highlightEpisode(view: View) {
for (i in 0 until episodeList.childCount) {
episodeList.getChildAt(i).isSelected = (episodeList.getChildAt(i) == view)
}
}
private fun showLoading(show: Boolean) {
loadingIndicator.visibility = if (show) View.VISIBLE else View.GONE
}
private fun showError(msg: String) {
errorText.text = msg
errorText.visibility = View.VISIBLE
showLoading(false)
}
private fun setupTouchListeners() {
var panelVisible = true
playerView.setOnClickListener {
panelVisible = !panelVisible
episodePanel.visibility = if (panelVisible) View.VISIBLE else View.GONE
}
playerWebView.setOnClickListener {
panelVisible = !panelVisible
episodePanel.visibility = if (panelVisible) View.VISIBLE else View.GONE
}
}
override fun onPause() {
super.onPause()
exoPlayer?.playWhenReady = false
}
override fun onDestroy() {
super.onDestroy()
exoPlayer?.release()
exoPlayer = null
}
}