package com.videoapp.tv import android.os.Bundle import android.os.Handler import android.os.Looper import android.view.View import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Button import android.widget.ImageButton import android.widget.LinearLayout 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.PlaySource 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 controlPanel: View private lateinit var sourceList: LinearLayout private lateinit var episodeList: 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 sources: List = emptyList() private var currentSourceIndex = 0 private var currentEpisode: Episode? = null private val hideHandler = Handler(Looper.getMainLooper()) private val hideRunnable = Runnable { hideControls() } private var controlsVisible = true 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) controlPanel = findViewById(R.id.control_panel) sourceList = findViewById(R.id.source_list) 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") ?: "" btnClose.setOnClickListener { finish() } initExoPlayer() loadSources(detailUrl) setupTouchListeners() } private fun initExoPlayer() { exoPlayer = ExoPlayer.Builder(this).build().also { player -> playerView.player = player } } private fun loadSources(detailUrl: String) { showLoading(true) val config = configRepo.getConfig() lifecycleScope.launch { try { sources = videoExtractor.extractVideos(detailUrl, config) if (sources.isNotEmpty()) { buildSourceUI() selectSource(0) resetAutoHide() } else { tryPlayDirectly(detailUrl, config) } } catch (e: Exception) { showError("加载失败: ${e.message}") showLoading(false) } } } private fun buildSourceUI() { sourceList.removeAllViews() sources.forEachIndexed { index, source -> val btn = Button(this).apply { text = source.name 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 { selectSource(index) resetAutoHide() } } sourceList.addView(btn) } } private fun selectSource(index: Int) { if (index !in sources.indices) return currentSourceIndex = index // Highlight selected source for (i in 0 until sourceList.childCount) { sourceList.getChildAt(i).isSelected = (i == index) } // Build episode list for this source val source = sources[index] buildEpisodeUI(source.episodes) // Auto-play first episode if (source.episodes.isNotEmpty()) { playEpisode(source.episodes.first()) } } private fun buildEpisodeUI(eps: List) { episodeList.removeAllViews() eps.forEach { ep -> val btn = Button(this).apply { text = ep.title setBackgroundResource(R.drawable.episode_selector) setTextColor(ContextCompat.getColor(this@PlayerActivity, R.color.text_primary)) textSize = 12f minWidth = 0 setPadding(14, 6, 14, 6) isFocusable = true isFocusableInTouchMode = true setOnClickListener { highlightEpisode(it) playEpisode(ep) resetAutoHide() } } episodeList.addView(btn) } } private fun playEpisode(ep: Episode) { currentEpisode = ep 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) { controlPanel.visibility = View.GONE playWithExoPlayer(directUrl) } else if (iframeUrl != null) { controlPanel.visibility = View.GONE playWithWebView(iframeUrl) } else { controlPanel.visibility = View.GONE 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() { val listener = View.OnClickListener { toggleControls() } playerView.setOnClickListener(listener) playerWebView.setOnClickListener(listener) } private fun toggleControls() { if (controlsVisible) { hideControls() } else { showControls() resetAutoHide() } } private fun showControls() { controlsVisible = true btnClose.visibility = View.VISIBLE controlPanel.visibility = View.VISIBLE } private fun hideControls() { controlsVisible = false btnClose.visibility = View.GONE controlPanel.visibility = View.GONE } private fun resetAutoHide() { hideHandler.removeCallbacks(hideRunnable) if (!controlsVisible) { showControls() } hideHandler.postDelayed(hideRunnable, 4000) } override fun onPause() { super.onPause() exoPlayer?.playWhenReady = false } override fun onDestroy() { super.onDestroy() hideHandler.removeCallbacks(hideRunnable) exoPlayer?.release() exoPlayer = null } }