import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:fladder/models/items/media_segments_model.dart'; import 'package:fladder/models/settings/key_combinations.dart'; import 'package:fladder/util/bitrate_helper.dart'; import 'package:fladder/util/localization_helper.dart'; part 'video_player_settings.freezed.dart'; part 'video_player_settings.g.dart'; enum VideoHotKeys { playPause, seekForward, seekBack, mute, volumeUp, volumeDown, nextVideo, prevVideo, nextChapter, prevChapter, fullScreen, skipMediaSegment, exit; const VideoHotKeys(); String label(BuildContext context) { return switch (this) { VideoHotKeys.playPause => context.localized.playPause, VideoHotKeys.seekForward => context.localized.seekForward, VideoHotKeys.seekBack => context.localized.seekBack, VideoHotKeys.mute => context.localized.mute, VideoHotKeys.volumeUp => context.localized.volumeUp, VideoHotKeys.volumeDown => context.localized.volumeDown, VideoHotKeys.nextVideo => context.localized.nextVideo, VideoHotKeys.prevVideo => context.localized.prevVideo, VideoHotKeys.nextChapter => context.localized.nextChapter, VideoHotKeys.prevChapter => context.localized.prevChapter, VideoHotKeys.fullScreen => context.localized.fullScreen, VideoHotKeys.skipMediaSegment => context.localized.skipMediaSegment, VideoHotKeys.exit => context.localized.exit, }; } } @Freezed(copyWith: true) abstract class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel { const VideoPlayerSettingsModel._(); factory VideoPlayerSettingsModel({ double? screenBrightness, @Default(BoxFit.contain) BoxFit videoFit, @Default(false) bool fillScreen, @Default(true) bool hardwareAccel, @Default(false) bool useLibass, @Default(32) int bufferSize, PlayerOptions? playerOptions, @Default(100) double internalVolume, Set? allowedOrientations, @Default(AutoNextType.smart) AutoNextType nextVideoType, @Default(Bitrate.original) Bitrate maxHomeBitrate, @Default(Bitrate.original) Bitrate maxInternetBitrate, String? audioDevice, @Default(defaultSegmentSkipValues) Map segmentSkipSettings, @Default({}) Map hotKeys, }) = _VideoPlayerSettingsModel; double get volume => switch (defaultTargetPlatform) { TargetPlatform.android || TargetPlatform.iOS => 100, _ => internalVolume, }; factory VideoPlayerSettingsModel.fromJson(Map json) => _$VideoPlayerSettingsModelFromJson(json); PlayerOptions get wantedPlayer => playerOptions ?? PlayerOptions.platformDefaults; Map get currentShortcuts => _defaultVideoHotKeys.map((key, value) => MapEntry(key, hotKeys[key] ?? value)); Map get defaultShortCuts => _defaultVideoHotKeys; bool playerSame(VideoPlayerSettingsModel other) { return other.hardwareAccel == hardwareAccel && other.useLibass == useLibass && other.bufferSize == bufferSize && other.wantedPlayer == wantedPlayer; } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is VideoPlayerSettingsModel && other.screenBrightness == screenBrightness && other.videoFit == videoFit && other.fillScreen == fillScreen && other.hardwareAccel == hardwareAccel && other.useLibass == useLibass && other.bufferSize == bufferSize && other.internalVolume == internalVolume && other.playerOptions == playerOptions && other.audioDevice == audioDevice; } @override int get hashCode { return screenBrightness.hashCode ^ videoFit.hashCode ^ fillScreen.hashCode ^ hardwareAccel.hashCode ^ useLibass.hashCode ^ bufferSize.hashCode ^ internalVolume.hashCode ^ audioDevice.hashCode; } } enum PlayerOptions { libMDK, libMPV; const PlayerOptions(); static Iterable get available => kIsWeb ? {PlayerOptions.libMPV} : PlayerOptions.values; static PlayerOptions get platformDefaults { if (kIsWeb) return PlayerOptions.libMPV; return switch (defaultTargetPlatform) { _ => PlayerOptions.libMPV, }; } String label(BuildContext context) => switch (this) { PlayerOptions.libMDK => "MDK", PlayerOptions.libMPV => "MPV", }; } enum AutoNextType { off, smart, static; const AutoNextType(); String label(BuildContext context) => switch (this) { AutoNextType.off => context.localized.off, AutoNextType.smart => context.localized.autoNextOffSmartTitle, AutoNextType.static => context.localized.autoNextOffStaticTitle, }; String desc(BuildContext context) => switch (this) { AutoNextType.off => context.localized.off, AutoNextType.smart => context.localized.autoNextOffSmartDesc, AutoNextType.static => context.localized.autoNextOffStaticDesc, }; } Map get _defaultVideoHotKeys => { for (var hotKey in VideoHotKeys.values) hotKey: switch (hotKey) { VideoHotKeys.playPause => KeyCombination( key: LogicalKeyboardKey.space, altKey: LogicalKeyboardKey.keyK, ), VideoHotKeys.seekForward => KeyCombination( key: LogicalKeyboardKey.arrowRight, altKey: LogicalKeyboardKey.keyL, ), VideoHotKeys.seekBack => KeyCombination( key: LogicalKeyboardKey.arrowLeft, altKey: LogicalKeyboardKey.keyJ, ), VideoHotKeys.mute => KeyCombination(key: LogicalKeyboardKey.keyM), VideoHotKeys.volumeUp => KeyCombination(key: LogicalKeyboardKey.arrowUp), VideoHotKeys.volumeDown => KeyCombination(key: LogicalKeyboardKey.arrowDown), VideoHotKeys.prevVideo => KeyCombination(key: LogicalKeyboardKey.keyP, modifier: LogicalKeyboardKey.shiftLeft), VideoHotKeys.nextVideo => KeyCombination(key: LogicalKeyboardKey.keyN, modifier: LogicalKeyboardKey.shiftLeft), VideoHotKeys.nextChapter => KeyCombination(key: LogicalKeyboardKey.pageUp), VideoHotKeys.prevChapter => KeyCombination(key: LogicalKeyboardKey.pageDown), VideoHotKeys.fullScreen => KeyCombination(key: LogicalKeyboardKey.keyF), VideoHotKeys.skipMediaSegment => KeyCombination(key: LogicalKeyboardKey.keyS), VideoHotKeys.exit => KeyCombination(key: LogicalKeyboardKey.escape), }, };