Initial commit

This commit is contained in:
2025-07-13 14:01:29 +03:00
commit 0eaf91561a
188 changed files with 11616 additions and 0 deletions

View File

@@ -0,0 +1,368 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
import 'package:neomovies_mobile/data/models/player/video_source.dart';
import 'package:neomovies_mobile/data/models/player/video_quality.dart';
import 'package:neomovies_mobile/data/models/player/audio_track.dart';
import 'package:neomovies_mobile/data/models/player/subtitle.dart';
import 'package:neomovies_mobile/data/models/player/player_settings.dart';
class PlayerProvider with ChangeNotifier {
// Controller instances
VideoPlayerController? _videoPlayerController;
ChewieController? _chewieController;
// Player state
bool _isInitialized = false;
bool _isPlaying = false;
bool _isBuffering = false;
bool _isFullScreen = false;
bool _showControls = true;
Duration _position = Duration.zero;
Duration _duration = Duration.zero;
// Media info
String? _mediaId;
String? _mediaType;
String? _title;
String? _subtitle;
String? _posterUrl;
// Player settings
PlayerSettings _settings;
// Available options
List<VideoSource> _sources = [];
List<VideoQuality> _qualities = [];
List<AudioTrack> _audioTracks = [];
List<Subtitle> _subtitles = [];
// Selected options
VideoSource? _selectedSource;
VideoQuality? _selectedQuality;
AudioTrack? _selectedAudioTrack;
Subtitle? _selectedSubtitle;
// Playback state
double _volume = 1.0;
bool _isMuted = false;
double _playbackSpeed = 1.0;
// Getters
bool get isInitialized => _isInitialized;
bool get isPlaying => _isPlaying;
bool get isBuffering => _isBuffering;
bool get isFullScreen => _isFullScreen;
bool get showControls => _showControls;
Duration get position => _position;
Duration get duration => _duration;
String? get mediaId => _mediaId;
String? get mediaType => _mediaType;
String? get title => _title;
String? get subtitle => _subtitle;
String? get posterUrl => _posterUrl;
PlayerSettings get settings => _settings;
List<VideoSource> get sources => _sources;
List<VideoQuality> get qualities => _qualities;
List<AudioTrack> get audioTracks => _audioTracks;
List<Subtitle> get subtitles => _subtitles;
VideoSource? get selectedSource => _selectedSource;
VideoQuality? get selectedQuality => _selectedQuality;
AudioTrack? get selectedAudioTrack => _selectedAudioTrack;
Subtitle? get selectedSubtitle => _selectedSubtitle;
double get volume => _volume;
bool get isMuted => _isMuted;
double get playbackSpeed => _playbackSpeed;
// Controllers
VideoPlayerController? get videoPlayerController => _videoPlayerController;
ChewieController? get chewieController => _chewieController;
// Constructor
PlayerProvider({PlayerSettings? initialSettings})
: _settings = initialSettings ?? PlayerSettings.defaultSettings();
// Initialize the player with media
Future<void> initialize({
required String mediaId,
required String mediaType,
String? title,
String? subtitle,
String? posterUrl,
List<VideoSource>? sources,
List<VideoQuality>? qualities,
List<AudioTrack>? audioTracks,
List<Subtitle>? subtitles,
}) async {
_mediaId = mediaId;
_mediaType = mediaType;
_title = title;
_subtitle = subtitle;
_posterUrl = posterUrl;
// Set available options
_sources = sources ?? [];
_qualities = qualities ?? VideoQuality.defaultQualities;
_audioTracks = audioTracks ?? [];
_subtitles = subtitles ?? [];
// Set default selections
_selectedSource = _sources.isNotEmpty ? _sources.first : null;
_selectedQuality = _qualities.isNotEmpty ? _qualities.first : null;
_selectedAudioTrack = _audioTracks.isNotEmpty ? _audioTracks.first : null;
_selectedSubtitle = _subtitles.firstWhere(
(s) => s.id == 'none',
orElse: () => _subtitles.first,
);
// Initialize video player with the first source and quality
if (_selectedSource != null && _selectedQuality != null) {
await _initializeVideoPlayer();
}
_isInitialized = true;
notifyListeners();
}
// Initialize video player with current source and quality
Future<void> _initializeVideoPlayer() async {
if (_selectedSource == null || _selectedQuality == null) return;
// Dispose of previous controllers if they exist
await dispose();
try {
// In a real app, you would fetch the actual video URL based on source and quality
final videoUrl = _getVideoUrl(_selectedSource!, _selectedQuality!);
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse(videoUrl),
videoPlayerOptions: VideoPlayerOptions(
mixWithOthers: true,
),
);
await _videoPlayerController!.initialize();
// Setup position listener
_videoPlayerController!.addListener(_videoPlayerListener);
// Setup chewie controller
_setupChewieController();
// Start playing if autoplay is enabled
if (_settings.autoPlay) {
await _videoPlayerController!.play();
_isPlaying = true;
}
notifyListeners();
} catch (e) {
debugPrint('Error initializing video player: $e');
// Handle error appropriately
rethrow;
}
}
// Setup Chewie controller with custom options
void _setupChewieController() {
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController!,
autoPlay: _settings.autoPlay,
looping: false,
allowFullScreen: true,
allowMuting: true,
allowPlaybackSpeedChanging: true,
showControls: _settings.showControlsOnStart,
showControlsOnInitialize: _settings.showControlsOnStart,
placeholder: _posterUrl != null ? Image.network(_posterUrl!) : null,
aspectRatio: _videoPlayerController!.value.aspectRatio,
// Custom options can be added here
);
// Listen to Chewie events
_chewieController!.addListener(() {
if (_chewieController!.isFullScreen != _isFullScreen) {
_isFullScreen = _chewieController!.isFullScreen;
notifyListeners();
}
if (_chewieController!.isPlaying != _isPlaying) {
_isPlaying = _chewieController!.isPlaying;
notifyListeners();
}
});
}
// Video player listener
void _videoPlayerListener() {
if (!_videoPlayerController!.value.isInitialized) return;
final controller = _videoPlayerController!;
// Update buffering state
final isBuffering = controller.value.isBuffering;
if (_isBuffering != isBuffering) {
_isBuffering = isBuffering;
notifyListeners();
}
// Update position and duration
if (controller.value.duration != _duration) {
_duration = controller.value.duration;
}
if (controller.value.position != _position) {
_position = controller.value.position;
notifyListeners();
}
}
// Get video URL based on source and quality
// In a real app, this would make an API call to get the stream URL
String _getVideoUrl(VideoSource source, VideoQuality quality) {
// This is a placeholder - replace with actual logic to get the video URL
return 'https://example.com/stream/$mediaType/$mediaId?source=${source.name.toLowerCase()}&quality=${quality.name}';
}
// Toggle play/pause
Future<void> togglePlayPause() async {
if (_videoPlayerController == null) return;
if (_isPlaying) {
await _videoPlayerController!.pause();
} else {
await _videoPlayerController!.play();
}
_isPlaying = !_isPlaying;
notifyListeners();
}
// Seek to a specific position
Future<void> seekTo(Duration position) async {
if (_videoPlayerController == null) return;
await _videoPlayerController!.seekTo(position);
_position = position;
notifyListeners();
}
// Set volume (0.0 to 1.0)
Future<void> setVolume(double volume) async {
if (_videoPlayerController == null) return;
_volume = volume.clamp(0.0, 1.0);
await _videoPlayerController!.setVolume(_isMuted ? 0.0 : _volume);
notifyListeners();
}
// Toggle mute
Future<void> toggleMute() async {
if (_videoPlayerController == null) return;
_isMuted = !_isMuted;
await _videoPlayerController!.setVolume(_isMuted ? 0.0 : _volume);
notifyListeners();
}
// Set playback speed
Future<void> setPlaybackSpeed(double speed) async {
if (_videoPlayerController == null) return;
_playbackSpeed = speed;
await _videoPlayerController!.setPlaybackSpeed(speed);
notifyListeners();
}
// Change video source
Future<void> setSource(VideoSource source) async {
if (_selectedSource == source) return;
_selectedSource = source;
await _initializeVideoPlayer();
notifyListeners();
}
// Change video quality
Future<void> setQuality(VideoQuality quality) async {
if (_selectedQuality == quality) return;
_selectedQuality = quality;
await _initializeVideoPlayer();
notifyListeners();
}
// Change audio track
void setAudioTrack(AudioTrack track) {
if (_selectedAudioTrack == track) return;
_selectedAudioTrack = track;
// In a real implementation, you would update the audio track on the video player
notifyListeners();
}
// Change subtitle
void setSubtitle(Subtitle subtitle) {
if (_selectedSubtitle == subtitle) return;
_selectedSubtitle = subtitle;
// In a real implementation, you would update the subtitle on the video player
notifyListeners();
}
// Toggle fullscreen
void toggleFullScreen() {
if (_chewieController == null) return;
_isFullScreen = !_isFullScreen;
if (_isFullScreen) {
_chewieController!.enterFullScreen();
} else {
_chewieController!.exitFullScreen();
}
notifyListeners();
}
// Toggle controls visibility
void toggleControls() {
_showControls = !_showControls;
notifyListeners();
}
// Update player settings
void updateSettings(PlayerSettings newSettings) {
_settings = newSettings;
// Apply settings that affect the current playback
if (_videoPlayerController != null) {
_videoPlayerController!.setPlaybackSpeed(_settings.playbackSpeed);
// Apply other settings as needed
}
notifyListeners();
}
// Clean up resources
@override
Future<void> dispose() async {
_videoPlayerController?.removeListener(_videoPlayerListener);
await _videoPlayerController?.dispose();
await _chewieController?.dispose();
_videoPlayerController = null;
_chewieController = null;
_isInitialized = false;
_isPlaying = false;
_isBuffering = false;
_isFullScreen = false;
_position = Duration.zero;
_duration = Duration.zero;
super.dispose();
}
}

View File

@@ -0,0 +1,212 @@
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:neomovies_mobile/data/models/player/video_source.dart';
class SettingsProvider with ChangeNotifier {
static const String _settingsKey = 'player_settings';
late PlayerSettings _settings;
final SharedPreferences _prefs;
SettingsProvider(this._prefs) {
// Load settings from shared preferences
_loadSettings();
}
PlayerSettings get settings => _settings;
// Load settings from shared preferences
void _loadSettings() {
try {
final settingsJson = _prefs.getString(_settingsKey);
if (settingsJson != null) {
_settings = PlayerSettings.fromMap(
Map<String, dynamic>.from(settingsJson as Map),
);
} else {
// Use default settings if no saved settings exist
_settings = PlayerSettings.defaultSettings();
// Save default settings
_saveSettings();
}
} catch (e) {
debugPrint('Error loading player settings: $e');
// Fallback to default settings on error
_settings = PlayerSettings.defaultSettings();
}
}
// Save settings to shared preferences
Future<void> _saveSettings() async {
try {
await _prefs.setString(
_settingsKey,
_settings.toMap().toString(),
);
} catch (e) {
debugPrint('Error saving player settings: $e');
}
}
// Update and save settings
Future<void> updateSettings(PlayerSettings newSettings) async {
if (_settings == newSettings) return;
_settings = newSettings;
await _saveSettings();
notifyListeners();
}
// Individual setting updates
// Video settings
Future<void> setAutoPlay(bool value) async {
if (_settings.autoPlay == value) return;
_settings = _settings.copyWith(autoPlay: value);
await _saveSettings();
notifyListeners();
}
Future<void> setAutoPlayNextEpisode(bool value) async {
if (_settings.autoPlayNextEpisode == value) return;
_settings = _settings.copyWith(autoPlayNextEpisode: value);
await _saveSettings();
notifyListeners();
}
Future<void> setSkipIntro(bool value) async {
if (_settings.skipIntro == value) return;
_settings = _settings.copyWith(skipIntro: value);
await _saveSettings();
notifyListeners();
}
Future<void> setSkipCredits(bool value) async {
if (_settings.skipCredits == value) return;
_settings = _settings.copyWith(skipCredits: value);
await _saveSettings();
notifyListeners();
}
Future<void> setRememberPlaybackPosition(bool value) async {
if (_settings.rememberPlaybackPosition == value) return;
_settings = _settings.copyWith(rememberPlaybackPosition: value);
await _saveSettings();
notifyListeners();
}
Future<void> setPlaybackSpeed(double value) async {
if (_settings.playbackSpeed == value) return;
_settings = _settings.copyWith(playbackSpeed: value);
await _saveSettings();
notifyListeners();
}
// Subtitle settings
Future<void> setDefaultSubtitleLanguage(String language) async {
if (_settings.defaultSubtitleLanguage == language) return;
_settings = _settings.copyWith(defaultSubtitleLanguage: language);
await _saveSettings();
notifyListeners();
}
Future<void> setSubtitleSize(double size) async {
if (_settings.subtitleSize == size) return;
_settings = _settings.copyWith(subtitleSize: size);
await _saveSettings();
notifyListeners();
}
Future<void> setSubtitleTextColor(String color) async {
if (_settings.subtitleTextColor == color) return;
_settings = _settings.copyWith(subtitleTextColor: color);
await _saveSettings();
notifyListeners();
}
Future<void> setSubtitleBackgroundColor(String color) async {
if (_settings.subtitleBackgroundColor == color) return;
_settings = _settings.copyWith(subtitleBackgroundColor: color);
await _saveSettings();
notifyListeners();
}
Future<void> setSubtitleBackgroundEnabled(bool enabled) async {
if (_settings.subtitleBackgroundEnabled == enabled) return;
_settings = _settings.copyWith(subtitleBackgroundEnabled: enabled);
await _saveSettings();
notifyListeners();
}
// Playback settings
Future<void> setDefaultQualityIndex(int index) async {
if (_settings.defaultQualityIndex == index) return;
_settings = _settings.copyWith(defaultQualityIndex: index);
await _saveSettings();
notifyListeners();
}
Future<void> setDataSaverMode(bool enabled) async {
if (_settings.dataSaverMode == enabled) return;
_settings = _settings.copyWith(dataSaverMode: enabled);
await _saveSettings();
notifyListeners();
}
Future<void> setDownloadOverWifiOnly(bool enabled) async {
if (_settings.downloadOverWifiOnly == enabled) return;
_settings = _settings.copyWith(downloadOverWifiOnly: enabled);
await _saveSettings();
notifyListeners();
}
// Player UI settings
Future<void> setShowControlsOnStart(bool show) async {
if (_settings.showControlsOnStart == show) return;
_settings = _settings.copyWith(showControlsOnStart: show);
await _saveSettings();
notifyListeners();
}
Future<void> setDoubleTapToSeek(bool enabled) async {
if (_settings.doubleTapToSeek == enabled) return;
_settings = _settings.copyWith(doubleTapToSeek: enabled);
await _saveSettings();
notifyListeners();
}
Future<void> setSwipeToSeek(bool enabled) async {
if (_settings.swipeToSeek == enabled) return;
_settings = _settings.copyWith(swipeToSeek: enabled);
await _saveSettings();
notifyListeners();
}
Future<void> setShowRemainingTime(bool show) async {
if (_settings.showRemainingTime == show) return;
_settings = _settings.copyWith(showRemainingTime: show);
await _saveSettings();
notifyListeners();
}
// Default video source
Future<void> setDefaultSource(VideoSource source) async {
_settings = _settings.copyWith(defaultSource: source);
await _saveSettings();
notifyListeners();
}
// Reset all settings to default
Future<void> resetToDefaults() async {
_settings = PlayerSettings.defaultSettings();
await _saveSettings();
notifyListeners();
}
// Clear all settings
Future<void> clear() async {
await _prefs.remove(_settingsKey);
_settings = PlayerSettings.defaultSettings();
notifyListeners();
}
}