Skip to content

Commit 1d04f6e

Browse files
committed
fix
1 parent 0aacc38 commit 1d04f6e

File tree

6 files changed

+394
-35
lines changed

6 files changed

+394
-35
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ android {
1818
// Android 7.0 (API 23) and earlier are not supported
1919
minSdk = 24
2020
targetSdk = 36
21-
versionCode = 20
22-
versionName = "1.1.9"
21+
versionCode = 21
22+
versionName = "2.0.0"
2323

2424
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2525

app/src/main/java/com/pira/ccloud/VideoPlayerActivity.kt

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import androidx.media3.ui.PlayerView
9292
import com.pira.ccloud.data.model.SubtitleSettings
9393
import com.pira.ccloud.data.model.VideoPlayerSettings
9494
import com.pira.ccloud.data.model.FontSettings
95+
import com.pira.ccloud.data.model.WatchedEpisode
9596
import com.pira.ccloud.utils.StorageUtils
9697
import com.pira.ccloud.ui.theme.FontManager
9798
import kotlinx.coroutines.CoroutineScope
@@ -131,6 +132,9 @@ fun PlayerView.setSubtitleColors(settings: SubtitleSettings, typeface: Typeface?
131132
class VideoPlayerActivity : ComponentActivity() {
132133
companion object {
133134
const val EXTRA_VIDEO_URL = "video_url"
135+
const val EXTRA_SERIES_ID = "series_id"
136+
const val EXTRA_SEASON_ID = "season_id"
137+
const val EXTRA_EPISODE_ID = "episode_id"
134138
const val REQUEST_WRITE_SETTINGS = 1001
135139

136140
fun start(context: Context, videoUrl: String) {
@@ -139,12 +143,26 @@ class VideoPlayerActivity : ComponentActivity() {
139143
}
140144
context.startActivity(intent)
141145
}
146+
147+
fun startWithEpisodeInfo(context: Context, videoUrl: String, seriesId: Int, seasonId: Int, episodeId: Int) {
148+
val intent = Intent(context, VideoPlayerActivity::class.java).apply {
149+
putExtra(EXTRA_VIDEO_URL, videoUrl)
150+
putExtra(EXTRA_SERIES_ID, seriesId)
151+
putExtra(EXTRA_SEASON_ID, seasonId)
152+
putExtra(EXTRA_EPISODE_ID, episodeId)
153+
}
154+
context.startActivity(intent)
155+
}
142156
}
143157

144158
private var exoPlayer: ExoPlayer? = null
145159
private var videoUrl: String? = null
160+
private var seriesId: Int? = null
161+
private var seasonId: Int? = null
162+
private var episodeId: Int? = null
146163
private var playerInitialized = false
147164
private var isActivityResumed = false
165+
private var hasMarkedAsWatched = false
148166

149167
override fun onCreate(savedInstanceState: Bundle?) {
150168
super.onCreate(savedInstanceState)
@@ -159,10 +177,19 @@ class VideoPlayerActivity : ComponentActivity() {
159177
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
160178

161179
videoUrl = intent.getStringExtra(EXTRA_VIDEO_URL)
180+
seriesId = intent.getIntExtra(EXTRA_SERIES_ID, -1).takeIf { it != -1 }
181+
seasonId = intent.getIntExtra(EXTRA_SEASON_ID, -1).takeIf { it != -1 }
182+
episodeId = intent.getIntExtra(EXTRA_EPISODE_ID, -1).takeIf { it != -1 }
162183

163184
if (videoUrl != null) {
164185
setContent {
165-
VideoPlayerScreen(videoUrl!!, this::finish) { player ->
186+
VideoPlayerScreen(
187+
videoUrl = videoUrl!!,
188+
seriesId = seriesId,
189+
seasonId = seasonId,
190+
episodeId = episodeId,
191+
onBack = this::finish
192+
) { player ->
166193
exoPlayer = player
167194
playerInitialized = true
168195
}
@@ -306,6 +333,9 @@ class VideoPlayerActivity : ComponentActivity() {
306333
@Composable
307334
fun VideoPlayerScreen(
308335
videoUrl: String,
336+
seriesId: Int?,
337+
seasonId: Int?,
338+
episodeId: Int?,
309339
onBack: () -> Unit,
310340
onPlayerReady: (ExoPlayer) -> Unit
311341
) {
@@ -323,6 +353,7 @@ fun VideoPlayerScreen(
323353
var playbackSpeed by remember { mutableStateOf(1.0f) }
324354
var showSpeedDropdown by remember { mutableStateOf(false) }
325355
var playerInitialized by remember { mutableStateOf(false) }
356+
var hasMarkedAsWatched by remember { mutableStateOf(false) }
326357

327358
// Track selection state
328359
var showTrackSelectionDialog by remember { mutableStateOf(false) }
@@ -396,7 +427,7 @@ fun VideoPlayerScreen(
396427
if (isRetrying && currentPosition > 0) {
397428
seekTo(currentPosition)
398429
}
399-
playWhenReady = true // Start playing by default
430+
playWhenReady = isPlaying // Start with current play state
400431
// Set initial playback speed
401432
setPlaybackSpeed(playbackSpeed)
402433
} catch (e: Exception) {
@@ -450,10 +481,25 @@ fun VideoPlayerScreen(
450481
}
451482
}
452483

453-
// Update player state
484+
// Update player state and mark episode as watched
454485
LaunchedEffect(isPlaying, exoPlayer) {
455486
try {
456487
exoPlayer?.playWhenReady = isPlaying
488+
489+
// Mark episode as watched when playback starts (only once)
490+
if (isPlaying && !hasMarkedAsWatched && seriesId != null && seasonId != null && episodeId != null) {
491+
try {
492+
val watchedEpisode = WatchedEpisode(
493+
seriesId = seriesId!!,
494+
seasonId = seasonId!!,
495+
episodeId = episodeId!!
496+
)
497+
StorageUtils.saveWatchedEpisode(context, watchedEpisode)
498+
hasMarkedAsWatched = true
499+
} catch (e: Exception) {
500+
// Ignore storage errors
501+
}
502+
}
457503
} catch (e: Exception) {
458504
// Ignore player state errors
459505
}
@@ -482,6 +528,15 @@ fun VideoPlayerScreen(
482528
try {
483529
if (playbackState == Player.STATE_READY) {
484530
duration = exoPlayer?.duration ?: 0L
531+
532+
// After the player is ready (especially after a retry),
533+
// ensure the playWhenReady state is consistent with our UI state
534+
if (exoPlayer != null && !isRetrying) {
535+
exoPlayer?.playWhenReady = isPlaying
536+
}
537+
} else if (playbackState == Player.STATE_ENDED) {
538+
// Video ended, pause the player
539+
isPlaying = false
485540
}
486541
} catch (e: Exception) {
487542
// Ignore duration errors
@@ -509,6 +564,7 @@ fun VideoPlayerScreen(
509564

510565
// Store current position before retrying
511566
val retryPosition = currentPosition
567+
val wasPlaying = isPlaying // Store whether it was playing before the error
512568

513569
// Attempt to retry after a delay
514570
CoroutineScope(Dispatchers.Main).launch {
@@ -520,7 +576,12 @@ fun VideoPlayerScreen(
520576
player.prepare()
521577
// Seek to the stored position after preparing
522578
player.seekTo(retryPosition)
523-
player.playWhenReady = true
579+
580+
// Resume playback if it was playing before the error
581+
player.playWhenReady = wasPlaying
582+
583+
// Update the UI state to match the player state
584+
isPlaying = wasPlaying
524585
isRetrying = false
525586
playerError = null
526587
}
@@ -963,13 +1024,17 @@ fun VideoPlayerScreen(
9631024
// Manual retry
9641025
try {
9651026
exoPlayer?.let { player ->
966-
// Store current position before retrying
1027+
// Store current position and playback state before retrying
9671028
val retryPosition = currentPosition
1029+
val wasPlaying = isPlaying // Store whether it was playing before the retry
9681030
player.setMediaItem(MediaItem.fromUri(Uri.parse(videoUrl)))
9691031
player.prepare()
9701032
// Seek to the stored position after preparing
9711033
player.seekTo(retryPosition)
972-
player.playWhenReady = true
1034+
// Resume playback if it was playing before the retry
1035+
player.playWhenReady = wasPlaying
1036+
// Update the UI state to match the player state
1037+
isPlaying = wasPlaying
9731038
isRetrying = false
9741039
playerError = null
9751040
}

app/src/main/java/com/pira/ccloud/data/model/Series.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,12 @@ data class Episode(
5252
val duration: String?,
5353
val image: String,
5454
val sources: List<Source>
55+
)
56+
57+
@Serializable
58+
data class WatchedEpisode(
59+
val seriesId: Int,
60+
val seasonId: Int,
61+
val episodeId: Int,
62+
val watchedAt: Long = System.currentTimeMillis()
5563
)

0 commit comments

Comments
 (0)