diff --git a/lib/mobile/features/iptv/screens/native_player_screen.dart b/lib/mobile/features/iptv/screens/native_player_screen.dart index 52608d7..d9c8384 100644 --- a/lib/mobile/features/iptv/screens/native_player_screen.dart +++ b/lib/mobile/features/iptv/screens/native_player_screen.dart @@ -263,22 +263,31 @@ class _NativePlayerScreenState extends ConsumerState { /// Save resume position when exiting player void _saveResumePositionOnExit() { if (widget.streamType == StreamType.live) return; - if (_duration.inSeconds <= 0) return; - final progress = _position.inSeconds / _duration.inSeconds; + debugPrint('MediaKitPlayer: Saving position - pos: ${_position.inSeconds}s, dur: ${_duration.inSeconds}s, contentId: $_contentId'); - // Don't save if almost finished (> 90%) or already marked as watched - if (progress > 0.90 || _hasMarkedAsWatched) { + // If already marked as watched, clear any saved position + if (_hasMarkedAsWatched) { ref.read(mobileWatchHistoryProvider.notifier).clearResumePosition(_contentId); + debugPrint('MediaKitPlayer: Already watched, clearing position'); return; } - // Save position + // Check if almost finished (> 90%) + if (_duration.inSeconds > 0) { + final progress = _position.inSeconds / _duration.inSeconds; + if (progress > 0.90) { + ref.read(mobileWatchHistoryProvider.notifier).clearResumePosition(_contentId); + debugPrint('MediaKitPlayer: Almost finished (${(progress * 100).toStringAsFixed(1)}%), clearing position'); + return; + } + } + + // Save position (saveResumePosition handles the 30s minimum check) ref.read(mobileWatchHistoryProvider.notifier).saveResumePosition( _contentId, _position.inSeconds, ); - debugPrint('MediaKitPlayer: Saved resume position at ${_position.inSeconds}s'); } /// Attempt to reconnect a live stream that stopped diff --git a/lib/mobile/providers/mobile_settings_providers.dart b/lib/mobile/providers/mobile_settings_providers.dart index c235281..f59678b 100644 --- a/lib/mobile/providers/mobile_settings_providers.dart +++ b/lib/mobile/providers/mobile_settings_providers.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; @@ -166,12 +167,24 @@ class MobileWatchHistoryNotifier extends StateNotifier { final movies = _box?.get('watchedMovies', defaultValue: []) as List?; final episodes = _box?.get('watchedEpisodes', defaultValue: []) as List?; - final positions = _box?.get('resumePositions', defaultValue: {}) as Map?; + final positionsRaw = _box?.get('resumePositions'); + + // Safe conversion for resumePositions map + Map positions = {}; + if (positionsRaw != null && positionsRaw is Map) { + positionsRaw.forEach((key, value) { + if (key is String && value is int) { + positions[key] = value; + } else if (key is String && value is num) { + positions[key] = value.toInt(); + } + }); + } state = MobileWatchHistory( watchedMovies: movies?.cast().toSet() ?? {}, watchedEpisodes: episodes?.cast().toSet() ?? {}, - resumePositions: positions?.cast() ?? {}, + resumePositions: positions, ); } @@ -208,12 +221,16 @@ class MobileWatchHistoryNotifier extends StateNotifier { /// Save resume position for movie or episode void saveResumePosition(String contentId, int positionSeconds) { - // Only save if position is meaningful (> 30 seconds and < 95% of typical content) - if (positionSeconds < 30) return; + // Only save if position is meaningful (> 30 seconds) + if (positionSeconds < 30) { + debugPrint('[WatchHistory] Position too short ($positionSeconds), skipping save'); + return; + } + debugPrint('[WatchHistory] Saving position for $contentId: ${positionSeconds}s'); final positions = {...state.resumePositions, contentId: positionSeconds}; state = state.copyWith(resumePositions: positions); - _box?.put('resumePositions', positions); + _box?.put('resumePositions', Map.from(positions)); } /// Clear resume position (when content finished or user wants to restart)