Skip to content

Commit ff1be65

Browse files
author
linshen
committed
支持多语言难度和字幕滚动
1 parent 0c8e6af commit ff1be65

19 files changed

+2587
-1124
lines changed

lib/config/style_config.dart

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.

lib/main.dart

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'screens/register_screen.dart';
1414
import 'screens/settings_screen.dart';
1515
import 'providers/navigation_provider.dart';
1616
import 'screens/main_screen.dart';
17+
import 'config/style_config.dart';
1718

1819
// 添加全局 navigatorKey
1920
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@@ -24,7 +25,7 @@ void main() async {
2425

2526
final settingsProvider = SettingsProvider();
2627
await settingsProvider.init();
27-
28+
2829
final apiService = ApiService(settingsProvider);
2930
final authProvider = AuthProvider(apiService);
3031
await authProvider.init();
@@ -60,17 +61,10 @@ class MyApp extends StatelessWidget {
6061
return Consumer2<ThemeProvider, AuthProvider>(
6162
builder: (context, themeProvider, authProvider, child) {
6263
return MaterialApp(
63-
navigatorKey: navigatorKey, // 添加 navigatorKey
64+
navigatorKey: navigatorKey,
6465
title: 'LingoPod 译播客',
65-
theme: ThemeData(
66-
useMaterial3: true,
67-
platform: TargetPlatform.iOS,
68-
),
69-
darkTheme: ThemeData(
70-
useMaterial3: true,
71-
platform: TargetPlatform.iOS,
72-
brightness: Brightness.dark,
73-
),
66+
theme: StyleConfig.getLightTheme(),
67+
darkTheme: StyleConfig.getDarkTheme(),
7468
themeMode: themeProvider.themeMode,
7569
localizationsDelegates: const [
7670
GlobalMaterialLocalizations.delegate,
@@ -81,7 +75,22 @@ class MyApp extends StatelessWidget {
8175
Locale('zh', 'CN'),
8276
Locale('en', 'US'),
8377
],
84-
home: authProvider.isAuthenticated
78+
builder: (context, child) {
79+
// 添加全局响应式布局支持
80+
return MediaQuery(
81+
data: MediaQuery.of(context).copyWith(
82+
textScaleFactor: 1.0,
83+
),
84+
child: ScrollConfiguration(
85+
behavior: ScrollConfiguration.of(context).copyWith(
86+
physics: const BouncingScrollPhysics(),
87+
scrollbars: true,
88+
),
89+
child: child!,
90+
),
91+
);
92+
},
93+
home: authProvider.isAuthenticated
8594
? const MainScreen()
8695
: const LoginScreen(),
8796
routes: {

lib/models/podcast.dart

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'style_params.dart';
2+
13
class Podcast {
24
final String taskId;
35
final String url;
@@ -8,17 +10,20 @@ class Podcast {
810
final int currentStepIndex;
911
final int totalSteps;
1012
final int stepProgress;
11-
final String audioUrlCn;
12-
final String audioUrlEn;
13-
final String subtitleUrlCn;
14-
final String subtitleUrlEn;
1513
final bool isPublic;
1614
final int userId;
1715
final int createdBy;
1816
final int? updatedBy;
1917
final int createdAt;
2018
final int updatedAt;
2119
final String progressMessage;
20+
final StyleParams styleParams;
21+
final Map<String, Map<String, Map<String, String>>> files;
22+
23+
String? get audioUrlCn => files['elementary']?['cn']?['audio'];
24+
String? get audioUrlEn => files['elementary']?['en']?['audio'];
25+
String? get subtitleUrlCn => files['elementary']?['cn']?['subtitle'];
26+
String? get subtitleUrlEn => files['elementary']?['en']?['subtitle'];
2227

2328
Podcast({
2429
required this.taskId,
@@ -30,18 +35,17 @@ class Podcast {
3035
required this.currentStepIndex,
3136
required this.totalSteps,
3237
required this.stepProgress,
33-
required this.audioUrlCn,
34-
required this.audioUrlEn,
35-
required this.subtitleUrlCn,
36-
required this.subtitleUrlEn,
3738
required this.isPublic,
3839
required this.userId,
3940
required this.createdBy,
4041
this.updatedBy,
4142
required this.createdAt,
4243
required this.updatedAt,
4344
required this.progressMessage,
44-
});
45+
StyleParams? styleParams,
46+
Map<String, Map<String, Map<String, String>>>? files,
47+
}) : styleParams = styleParams ?? StyleParams(),
48+
files = files ?? {};
4549

4650
factory Podcast.fromJson(Map<String, dynamic> json) {
4751
// 首先检查必需字段
@@ -52,15 +56,22 @@ class Podcast {
5256

5357
// 检查已完成任务的音频和字幕文件
5458
final status = json['status'] ?? 'pending';
55-
final audioUrlCn = json['audioUrlCn'];
56-
final audioUrlEn = json['audioUrlEn'];
57-
final subtitleUrlCn = json['subtitleUrlCn'];
58-
final subtitleUrlEn = json['subtitleUrlEn'];
59-
60-
if (status == 'completed' &&
61-
(audioUrlCn == null || audioUrlEn == null ||
62-
subtitleUrlCn == null || subtitleUrlEn == null)) {
63-
throw Exception('已完成的任务缺少必要的音频或字幕文件');
59+
final files = json['files'] != null
60+
? _parseFiles(json['files'])
61+
: <String, Map<String, Map<String, String>>>{};
62+
63+
if (status == 'completed') {
64+
final audioUrlCn = files['elementary']?['cn']?['audio'];
65+
final audioUrlEn = files['elementary']?['en']?['audio'];
66+
final subtitleUrlCn = files['elementary']?['cn']?['subtitle'];
67+
final subtitleUrlEn = files['elementary']?['en']?['subtitle'];
68+
69+
if (audioUrlCn == null ||
70+
audioUrlEn == null ||
71+
subtitleUrlCn == null ||
72+
subtitleUrlEn == null) {
73+
throw Exception('已完成的任务缺少必要的音频或字幕文件');
74+
}
6475
}
6576

6677
return Podcast(
@@ -73,20 +84,20 @@ class Podcast {
7384
currentStepIndex: json['current_step_index'] ?? 0,
7485
totalSteps: json['total_steps'] ?? 0,
7586
stepProgress: json['step_progress'] ?? 0,
76-
audioUrlCn: audioUrlCn ?? '',
77-
audioUrlEn: audioUrlEn ?? '',
78-
subtitleUrlCn: subtitleUrlCn ?? '',
79-
subtitleUrlEn: subtitleUrlEn ?? '',
8087
isPublic: json['is_public'] ?? false,
8188
userId: json['user_id'] ?? 0,
8289
createdBy: json['created_by'] ?? 0,
8390
updatedBy: json['updated_by'],
84-
createdAt: json['createdAt'] ?? 0,
85-
updatedAt: json['updatedAt'] ?? 0,
91+
createdAt: json['created_at'] ?? 0,
92+
updatedAt: json['updated_at'] ?? 0,
8693
progressMessage: json['progress_message'] ?? '',
94+
styleParams: json['style_params'] != null
95+
? StyleParams.fromJson(json['style_params'])
96+
: null,
97+
files: files,
8798
);
8899
}
89-
100+
90101
Map<String, dynamic> toJson() {
91102
return {
92103
'taskId': taskId,
@@ -98,17 +109,38 @@ class Podcast {
98109
'current_step_index': currentStepIndex,
99110
'total_steps': totalSteps,
100111
'step_progress': stepProgress,
101-
'audio_url_cn': audioUrlCn,
102-
'audio_url_en': audioUrlEn,
103-
'subtitle_url_cn': subtitleUrlCn,
104-
'subtitle_url_en': subtitleUrlEn,
105112
'is_public': isPublic,
106113
'user_id': userId,
107114
'created_by': createdBy,
108115
'updated_by': updatedBy,
109116
'created_at': createdAt,
110117
'updated_at': updatedAt,
111118
'progress_message': progressMessage,
119+
'style_params': styleParams.toJson(),
120+
'files': files,
112121
};
113122
}
114-
}
123+
124+
static Map<String, Map<String, Map<String, String>>> _parseFiles(
125+
Map<String, dynamic> json) {
126+
final result = <String, Map<String, Map<String, String>>>{};
127+
128+
json.forEach((level, levelData) {
129+
if (levelData is Map) {
130+
result[level] = {};
131+
(levelData as Map<String, dynamic>).forEach((lang, langData) {
132+
if (langData is Map) {
133+
result[level]![lang] = {};
134+
(langData as Map<String, dynamic>).forEach((type, url) {
135+
if (url is String) {
136+
result[level]![lang]![type] = url;
137+
}
138+
});
139+
}
140+
});
141+
}
142+
});
143+
144+
return result;
145+
}
146+
}

lib/models/style_params.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
class StyleParams {
2+
final String contentLength;
3+
final String tone;
4+
final String emotion;
5+
6+
StyleParams({
7+
this.contentLength = 'medium',
8+
this.tone = 'casual',
9+
this.emotion = 'neutral',
10+
});
11+
12+
factory StyleParams.fromJson(Map<String, dynamic> json) {
13+
return StyleParams(
14+
contentLength: json['content_length'] ?? 'medium',
15+
tone: json['tone'] ?? 'casual',
16+
emotion: json['emotion'] ?? 'neutral',
17+
);
18+
}
19+
20+
Map<String, dynamic> toJson() {
21+
return {
22+
'content_length': contentLength,
23+
'tone': tone,
24+
'emotion': emotion,
25+
};
26+
}
27+
28+
StyleParams copyWith({
29+
String? contentLength,
30+
String? tone,
31+
String? emotion,
32+
}) {
33+
return StyleParams(
34+
contentLength: contentLength ?? this.contentLength,
35+
tone: tone ?? this.tone,
36+
emotion: emotion ?? this.emotion,
37+
);
38+
}
39+
}

0 commit comments

Comments
 (0)