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 = 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) { 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 } }