mirror of
https://github.com/madari-media/madari-oss.git
synced 2026-03-11 17:15:39 +00:00
Merge pull request #49 from Abinanthankv/main
Subtitle customization for android.
This commit is contained in:
commit
6eeaf5b682
5 changed files with 207 additions and 6 deletions
|
|
@ -423,11 +423,33 @@ class _VideoViewerState extends State<VideoViewer> {
|
|||
});
|
||||
},
|
||||
);
|
||||
|
||||
String subtitleStyleName = config.subtitleStyle ?? 'Normal';
|
||||
String subtitleStyleColor = config.subtitleColor ?? 'white';
|
||||
double subtitleSize = config.subtitleSize ;
|
||||
Color hexToColor(String hexColor) {
|
||||
final hexCode = hexColor.replaceAll('#', '');
|
||||
return Color(int.parse('0x$hexCode'));
|
||||
}
|
||||
FontStyle getFontStyleFromString(String styleName) {
|
||||
switch (styleName.toLowerCase()) {
|
||||
case 'italic':
|
||||
return FontStyle.italic;
|
||||
case 'normal': // Explicitly handle 'normal' (good practice)
|
||||
default: // Default case for any other string or null
|
||||
return FontStyle.normal;
|
||||
}
|
||||
}
|
||||
FontStyle currentFontStyle = getFontStyleFromString(subtitleStyleName);
|
||||
return MaterialVideoControlsTheme(
|
||||
fullscreen: mobile,
|
||||
normal: mobile,
|
||||
child: Video(
|
||||
subtitleViewConfiguration: SubtitleViewConfiguration(
|
||||
style: TextStyle(color: hexToColor(subtitleStyleColor),
|
||||
fontSize: subtitleSize,
|
||||
fontStyle: currentFontStyle,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
fit: isScaled ? BoxFit.fitWidth : BoxFit.fitHeight,
|
||||
pauseUponEnteringBackgroundMode: true,
|
||||
key: key,
|
||||
|
|
@ -598,4 +620,4 @@ int calculateSecondsFromProgress(
|
|||
final clampedProgress = progressPercentage.clamp(0.0, 100.0);
|
||||
final currentSeconds = (duration * (clampedProgress / 100)).round();
|
||||
return currentSeconds;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import 'package:pocketbase/pocketbase.dart';
|
|||
|
||||
import '../../../engine/engine.dart';
|
||||
import '../../../utils/load_language.dart';
|
||||
import 'package:flex_color_picker/flex_color_picker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class PlaybackSettingsScreen extends StatefulWidget {
|
||||
const PlaybackSettingsScreen({super.key});
|
||||
|
|
@ -21,13 +23,69 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
// Playback settings
|
||||
bool _autoPlay = true;
|
||||
double _playbackSpeed = 1.0;
|
||||
double _subtitleSize = 10.0;
|
||||
String _defaultAudioTrack = 'eng';
|
||||
String _defaultSubtitleTrack = 'eng';
|
||||
bool _enableExternalPlayer = true;
|
||||
String? _defaultPlayerId;
|
||||
bool _disabledSubtitle = false;
|
||||
|
||||
Map<String, String> _availableLanguages = {};
|
||||
final List<String> _subtitleStyle = [
|
||||
'Normal',
|
||||
'Italic',
|
||||
];
|
||||
String colorToHex(Color color) {
|
||||
return '#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}';
|
||||
}
|
||||
|
||||
Color hexToColor(String hexColor) {
|
||||
final hexCode = hexColor.replaceAll('#', '');
|
||||
return Color(int.parse('0x$hexCode'));
|
||||
}
|
||||
|
||||
Color _selectedSubtitleColor = Colors.yellow;
|
||||
|
||||
_showColorPickerDialog(BuildContext context) async {
|
||||
Color? color = await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Pick a Subtitle Color'),
|
||||
content: SingleChildScrollView(
|
||||
child: ColorPicker(
|
||||
color: _selectedSubtitleColor,
|
||||
onColorChanged: (Color color) {
|
||||
_selectedSubtitleColor = color;
|
||||
},
|
||||
// Remove pickerType
|
||||
enableShadesSelection: true,
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pop(_selectedSubtitleColor); // Return the color
|
||||
},
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
if (color != null) {
|
||||
setState(() {
|
||||
_selectedSubtitleColor = color;
|
||||
});
|
||||
_debouncedSave(); // Debounced save after color change
|
||||
}
|
||||
}
|
||||
|
||||
List<DropdownMenuItem<String>> get dropdown =>
|
||||
_availableLanguages.entries.map((item) {
|
||||
|
|
@ -64,6 +122,12 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
? playbackConfig.externalPlayerId![currentPlatform]
|
||||
: null;
|
||||
_disabledSubtitle = playbackConfig.disableSubtitle;
|
||||
final subtitleStyle = playbackConfig.subtitleStyle; // Get saved style
|
||||
_subtitleStyle.removeWhere((style) =>
|
||||
style == subtitleStyle); // Remove saved style from dropdown options
|
||||
_subtitleStyle.insert(0, subtitleStyle ?? "Normal");
|
||||
_selectedSubtitleColor = hexToColor(playbackConfig.subtitleColor!);
|
||||
_subtitleSize = playbackConfig.subtitleSize.toDouble();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -103,6 +167,9 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
'externalPlayer': _enableExternalPlayer,
|
||||
'externalPlayerId': extranalId,
|
||||
'disableSubtitle': _disabledSubtitle,
|
||||
'subtitleStyle': _subtitleStyle[0],
|
||||
'subtitleColor': colorToHex(_selectedSubtitleColor),
|
||||
'subtitleSize': _subtitleSize,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -134,6 +201,12 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dropdownstyle = _subtitleStyle.map((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList();
|
||||
if (_error != null) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
|
|
@ -178,6 +251,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
divisions: 18,
|
||||
label: '${_playbackSpeed.toStringAsFixed(2)}x',
|
||||
onChanged: (value) {
|
||||
HapticFeedback.mediumImpact();
|
||||
setState(() => _playbackSpeed =
|
||||
double.parse(value.toStringAsFixed(2)));
|
||||
_debouncedSave();
|
||||
|
|
@ -209,7 +283,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
_debouncedSave();
|
||||
},
|
||||
),
|
||||
if (!_disabledSubtitle)
|
||||
if (!_disabledSubtitle) ...[
|
||||
ListTile(
|
||||
title: const Text('Default Subtitle Track'),
|
||||
trailing: DropdownButton<String>(
|
||||
|
|
@ -223,6 +297,87 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
|||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Material( // Center the text
|
||||
|
||||
child: ConstrainedBox( // Prevent overflow
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8, // 80% of screen width
|
||||
),
|
||||
child: Text(
|
||||
'Sample Text',
|
||||
textAlign: TextAlign.center, // Center text within its box
|
||||
style: TextStyle(
|
||||
fontSize: _subtitleSize / 2,
|
||||
color: _selectedSubtitleColor,
|
||||
fontStyle: _subtitleStyle[0].toLowerCase() == 'italic'
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Subtitle Style'),
|
||||
|
||||
trailing: DropdownButton<String>(
|
||||
value: _subtitleStyle[0],
|
||||
items: dropdownstyle,
|
||||
onChanged: (value) {
|
||||
HapticFeedback.mediumImpact();
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
// <--- Crucial setState here
|
||||
_subtitleStyle.remove(value);
|
||||
_subtitleStyle.insert(0, value);
|
||||
});
|
||||
_debouncedSave();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Subtitle Color'),
|
||||
trailing: GestureDetector(
|
||||
// Use GestureDetector to make the color display tappable
|
||||
onTap: () => _showColorPickerDialog(context),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedSubtitleColor,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Font Size'),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Slider(
|
||||
value: _subtitleSize,
|
||||
min: 20.0,
|
||||
max: 60.0,
|
||||
divisions: 18,
|
||||
label: '${_subtitleSize.toStringAsFixed(2)}x',
|
||||
onChanged: (value) {
|
||||
HapticFeedback.mediumImpact();
|
||||
setState(() =>
|
||||
_subtitleSize = double.parse(value.toStringAsFixed(2)));
|
||||
_debouncedSave();
|
||||
},
|
||||
),
|
||||
Text('Current: ${_subtitleSize.toStringAsFixed(2)}x'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
const Divider(),
|
||||
if (!isWeb)
|
||||
SwitchListTile(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ Future<Map<String, String>> loadLanguages(BuildContext context) async {
|
|||
return availableLanguages;
|
||||
}
|
||||
|
||||
|
||||
PlaybackConfig getPlaybackConfig() {
|
||||
final user = AppEngine.engine.pb.authStore.record;
|
||||
if (user == null) {
|
||||
|
|
@ -52,7 +53,10 @@ class PlaybackConfig {
|
|||
@JsonKey(defaultValue: false)
|
||||
final bool externalPlayer;
|
||||
final Map<String, String?>? externalPlayerId;
|
||||
|
||||
final String? subtitleStyle;
|
||||
final String? subtitleColor;
|
||||
@JsonKey(defaultValue: 10.0)
|
||||
final double subtitleSize;
|
||||
PlaybackConfig({
|
||||
required this.autoPlay,
|
||||
required this.playbackSpeed,
|
||||
|
|
@ -61,6 +65,9 @@ class PlaybackConfig {
|
|||
required this.externalPlayer,
|
||||
required this.disableSubtitle,
|
||||
this.externalPlayerId,
|
||||
this.subtitleStyle,
|
||||
this.subtitleColor,
|
||||
required this.subtitleSize,
|
||||
});
|
||||
|
||||
String? get currentPlayerPackage {
|
||||
|
|
|
|||
18
pubspec.lock
18
pubspec.lock
|
|
@ -542,6 +542,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flex_color_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flex_color_picker
|
||||
sha256: c083b79f1c57eaeed9f464368be376951230b3cb1876323b784626152a86e480
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
flex_seed_scheme:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flex_seed_scheme
|
||||
sha256: d3ba3c5c92d2d79d45e94b4c6c71d01fac3c15017da1545880c53864da5dfeb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
@ -1878,4 +1894,4 @@ packages:
|
|||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.3 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ dependencies:
|
|||
cast: ^2.1.0
|
||||
permission_handler: ^11.3.1
|
||||
android_intent_plus: ^5.2.1
|
||||
flex_color_picker: ^3.7.0
|
||||
|
||||
dependency_overrides:
|
||||
media_kit:
|
||||
|
|
|
|||
Loading…
Reference in a new issue