188 lines
4.9 KiB
Dart
188 lines
4.9 KiB
Dart
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
|
|
||
|
|
import 'shared/settings_persistence.dart';
|
||
|
|
|
||
|
|
|
||
|
|
const kLanguageNames = <String, String>{
|
||
|
|
'chi': '中文',
|
||
|
|
'zho': '中文',
|
||
|
|
'eng': '英语',
|
||
|
|
'jpn': '日语',
|
||
|
|
'kor': '韩语',
|
||
|
|
'fre': '法语',
|
||
|
|
'fra': '法语',
|
||
|
|
'ger': '德语',
|
||
|
|
'deu': '德语',
|
||
|
|
'spa': '西班牙语',
|
||
|
|
'ita': '意大利语',
|
||
|
|
'por': '葡萄牙语',
|
||
|
|
'rus': '俄语',
|
||
|
|
'ara': '阿拉伯语',
|
||
|
|
'hin': '印地语',
|
||
|
|
'tha': '泰语',
|
||
|
|
'vie': '越南语',
|
||
|
|
'pol': '波兰语',
|
||
|
|
'tur': '土耳其语',
|
||
|
|
'dut': '荷兰语',
|
||
|
|
'nld': '荷兰语',
|
||
|
|
'dan': '丹麦语',
|
||
|
|
'fin': '芬兰语',
|
||
|
|
'swe': '瑞典语',
|
||
|
|
'nor': '挪威语',
|
||
|
|
'nob': '书面挪威语',
|
||
|
|
'cat': '加泰罗尼亚语',
|
||
|
|
'cze': '捷克语',
|
||
|
|
'ces': '捷克语',
|
||
|
|
'hun': '匈牙利语',
|
||
|
|
'gre': '希腊语',
|
||
|
|
'ell': '希腊语',
|
||
|
|
'heb': '希伯来语',
|
||
|
|
'ind': '印度尼西亚语',
|
||
|
|
'may': '马来语',
|
||
|
|
'msa': '马来语',
|
||
|
|
'hrv': '克罗地亚语',
|
||
|
|
'baq': '巴斯克语',
|
||
|
|
'eus': '巴斯克语',
|
||
|
|
'glg': '加利西亚语',
|
||
|
|
'fil': '菲律宾语',
|
||
|
|
'rum': '罗马尼亚语',
|
||
|
|
'ron': '罗马尼亚语',
|
||
|
|
'bul': '保加利亚语',
|
||
|
|
'ukr': '乌克兰语',
|
||
|
|
'srp': '塞尔维亚语',
|
||
|
|
'slk': '斯洛伐克语',
|
||
|
|
'slv': '斯洛文尼亚语',
|
||
|
|
'lit': '立陶宛语',
|
||
|
|
'lav': '拉脱维亚语',
|
||
|
|
'est': '爱沙尼亚语',
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
class RememberedSubtitleTrack {
|
||
|
|
final int streamIndex;
|
||
|
|
final String? language;
|
||
|
|
final String? title;
|
||
|
|
final DateTime updatedAt;
|
||
|
|
|
||
|
|
const RememberedSubtitleTrack({
|
||
|
|
required this.streamIndex,
|
||
|
|
this.language,
|
||
|
|
this.title,
|
||
|
|
required this.updatedAt,
|
||
|
|
});
|
||
|
|
|
||
|
|
bool get isNone => streamIndex == -1;
|
||
|
|
|
||
|
|
Map<String, dynamic> toJson() => {
|
||
|
|
'streamIndex': streamIndex,
|
||
|
|
if (language != null) 'language': language,
|
||
|
|
if (title != null) 'title': title,
|
||
|
|
'updatedAt': updatedAt.toIso8601String(),
|
||
|
|
};
|
||
|
|
|
||
|
|
factory RememberedSubtitleTrack.fromJson(Map<String, dynamic> json) {
|
||
|
|
return RememberedSubtitleTrack(
|
||
|
|
streamIndex: (json['streamIndex'] as num?)?.toInt() ?? -1,
|
||
|
|
language: json['language'] as String?,
|
||
|
|
title: json['title'] as String?,
|
||
|
|
updatedAt:
|
||
|
|
DateTime.tryParse(json['updatedAt'] as String? ?? '') ??
|
||
|
|
DateTime.now(),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class SubtitleStyleSettings {
|
||
|
|
final int position;
|
||
|
|
final double fontSize;
|
||
|
|
final double delay;
|
||
|
|
final double bgOpacity;
|
||
|
|
|
||
|
|
const SubtitleStyleSettings({
|
||
|
|
this.position = 100,
|
||
|
|
this.fontSize = 55,
|
||
|
|
this.delay = 0.0,
|
||
|
|
this.bgOpacity = 0.0,
|
||
|
|
});
|
||
|
|
|
||
|
|
SubtitleStyleSettings copyWith({
|
||
|
|
int? position,
|
||
|
|
double? fontSize,
|
||
|
|
double? delay,
|
||
|
|
double? bgOpacity,
|
||
|
|
}) => SubtitleStyleSettings(
|
||
|
|
position: position ?? this.position,
|
||
|
|
fontSize: fontSize ?? this.fontSize,
|
||
|
|
delay: delay ?? this.delay,
|
||
|
|
bgOpacity: bgOpacity ?? this.bgOpacity,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
class SubtitleSettingsNotifier extends AsyncNotifier<SubtitleStyleSettings>
|
||
|
|
with SettingsPersistence {
|
||
|
|
static const _keyPosition = 'smplayer.subtitle_position';
|
||
|
|
static const _keyFontSize = 'smplayer.subtitle_font_size';
|
||
|
|
static const _keyDelay = 'smplayer.subtitle_delay';
|
||
|
|
static const _keyBgOpacity = 'smplayer.subtitle_bg_opacity';
|
||
|
|
|
||
|
|
static const _keyLastTracks = 'smplayer.subtitle_last_tracks_primary';
|
||
|
|
static const _maxRememberedTracks = 200;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<SubtitleStyleSettings> build() async {
|
||
|
|
final prefs = await getPrefs();
|
||
|
|
for (final stale in const [
|
||
|
|
'smplayer.subtitle_default_policy',
|
||
|
|
'smplayer.subtitle_preferred_language',
|
||
|
|
'smplayer.subtitle_secondary_position',
|
||
|
|
'smplayer.subtitle_secondary_font_size',
|
||
|
|
'smplayer.subtitle_secondary_delay',
|
||
|
|
'smplayer.subtitle_secondary_bg_opacity',
|
||
|
|
'smplayer.subtitle_secondary_follow_primary',
|
||
|
|
'smplayer.subtitle_last_tracks_secondary',
|
||
|
|
]) {
|
||
|
|
await prefs.remove(stale);
|
||
|
|
}
|
||
|
|
return SubtitleStyleSettings(
|
||
|
|
position: prefs.getInt(_keyPosition) ?? 100,
|
||
|
|
fontSize: prefs.getDouble(_keyFontSize) ?? 55,
|
||
|
|
delay: prefs.getDouble(_keyDelay) ?? 0.0,
|
||
|
|
bgOpacity: prefs.getDouble(_keyBgOpacity) ?? 0.0,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> setSettings(SubtitleStyleSettings settings) async {
|
||
|
|
state = AsyncValue.data(settings);
|
||
|
|
await persistFields({
|
||
|
|
_keyPosition: settings.position,
|
||
|
|
_keyFontSize: settings.fontSize,
|
||
|
|
_keyDelay: settings.delay,
|
||
|
|
_keyBgOpacity: settings.bgOpacity,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Future<void> saveLastSubtitleTrack(
|
||
|
|
String key,
|
||
|
|
RememberedSubtitleTrack? track,
|
||
|
|
) async {
|
||
|
|
if (track == null) return;
|
||
|
|
await putRemembered(
|
||
|
|
_keyLastTracks,
|
||
|
|
key,
|
||
|
|
track.toJson(),
|
||
|
|
_maxRememberedTracks,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<RememberedSubtitleTrack?> getLastSubtitleTrack(String key) async {
|
||
|
|
final entry = await readRemembered(_keyLastTracks, key);
|
||
|
|
return entry == null ? null : RememberedSubtitleTrack.fromJson(entry);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
final subtitleSettingsProvider =
|
||
|
|
AsyncNotifierProvider<SubtitleSettingsNotifier, SubtitleStyleSettings>(
|
||
|
|
SubtitleSettingsNotifier.new,
|
||
|
|
);
|