Skip to content

Commit df763f2

Browse files
committed
feat: improve the performance of the user interface and file list
1 parent 2fe6ba6 commit df763f2

28 files changed

Lines changed: 312 additions & 393 deletions

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ task downloadFiles(type: Exec) {
9292

9393
def calculatedMD5 = MessageDigest.getInstance("MD5").digest(Files.readAllBytes(destFile.toPath())).encodeHex().toString()
9494
if (calculatedMD5 != fileInfo.md5) {
95-
throw new FileNotFoundException("MD5 verification failed for ${destFile}")
95+
throw new GradleException("MD5 verification failed for ${destFile}")
9696
}
9797
}
9898
}

lib/hooks/use_cover.dart

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@ import 'package:collection/collection.dart';
22
import 'package:flutter_hooks/flutter_hooks.dart';
33
import 'package:flutter_zustand/flutter_zustand.dart';
44
import 'package:iris/models/file.dart';
5-
import 'package:iris/models/player.dart';
65
import 'package:iris/models/storages/local.dart';
76
import 'package:iris/models/storages/storage.dart';
87
import 'package:iris/store/use_play_queue_store.dart';
98
import 'package:iris/store/use_storage_store.dart';
109
import 'package:iris/utils/files_filter.dart';
11-
import 'package:provider/provider.dart';
1210

1311
FileItem? useCover() {
1412
final context = useContext();
15-
final isPlaying =
16-
context.select<MediaPlayer, bool>((player) => player.isPlaying);
1713

1814
final playQueue =
1915
usePlayQueueStore().select(context, (state) => state.playQueue);
@@ -24,10 +20,10 @@ FileItem? useCover() {
2420
() => playQueue.indexWhere((element) => element.index == currentIndex),
2521
[playQueue, currentIndex]);
2622

27-
final PlayQueueItem? currentPlay = useMemoized(
23+
final FileItem? file = useMemoized(
2824
() => playQueue.isEmpty || currentPlayIndex < 0
2925
? null
30-
: playQueue[currentPlayIndex],
26+
: playQueue[currentPlayIndex].file,
3127
[playQueue, currentPlayIndex]);
3228

3329
final localStoragesFuture =
@@ -37,28 +33,27 @@ FileItem? useCover() {
3733
final storages = useStorageStore().select(context, (state) => state.storages);
3834

3935
final Storage? storage = useMemoized(
40-
() => currentPlay?.file == null
36+
() => file == null
4137
? null
42-
: [...localStorages, ...storages].firstWhereOrNull(
43-
(storage) => storage.id == currentPlay?.file.storageId),
44-
[currentPlay?.file, localStorages, storages]);
38+
: [...localStorages, ...storages]
39+
.firstWhereOrNull((storage) => storage.id == file.storageId),
40+
[file, localStorages, storages]);
4541

4642
final cover = useState<FileItem?>(null);
4743

4844
useEffect(() {
4945
() async {
50-
final dir = currentPlay?.file == null || currentPlay!.file.path.isEmpty
51-
? <String>[]
52-
: ([...currentPlay.file.path]..removeLast());
53-
54-
if (storage == null || currentPlay?.file.type != ContentType.audio) {
46+
if (storage == null || file == null || file.type != ContentType.audio) {
5547
cover.value = null;
5648
return;
5749
}
5850

51+
final dir =
52+
file.path.isEmpty ? <String>[] : ([...file.path]..removeLast());
53+
5954
final files = await storage.getFiles(dir);
6055

61-
final images = filesFilter(files, [ContentType.image]);
56+
final images = filesFilter(files, types: [ContentType.image]);
6257

6358
cover.value = images.firstWhereOrNull((image) =>
6459
image.name.split('.').first.toLowerCase() == 'cover') ??
@@ -68,7 +63,7 @@ FileItem? useCover() {
6863
images.firstOrNull;
6964
}();
7065
return null;
71-
}, [storage, isPlaying]);
66+
}, [storage, file]);
7267

7368
return cover.value;
7469
}

lib/hooks/use_gesture.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Gesture useGesture({
9898

9999
void onDoubleTapDown(TapDownDetails details) {
100100
if (details.kind == PointerDeviceKind.touch) {
101-
final screenWidth = MediaQuery.of(context).size.width;
101+
final screenWidth = MediaQuery.sizeOf(context).width;
102102
final tapDx = details.globalPosition.dx;
103103

104104
if (tapDx > screenWidth * 0.7) {
@@ -195,7 +195,7 @@ Gesture useGesture({
195195

196196
if (details.kind == PointerDeviceKind.touch) {
197197
const double edgeDeadZone = 48.0;
198-
final screenSize = MediaQuery.of(context).size;
198+
final screenSize = MediaQuery.sizeOf(context);
199199
final startDx = details.globalPosition.dx;
200200

201201
if (startDx < edgeDeadZone || startDx > screenSize.width - edgeDeadZone) {
@@ -257,7 +257,7 @@ Gesture useGesture({
257257
// 仅在垂直滑动开始时判断一次左右区域
258258
if (!isLeftGesture.value && !isRightGesture.value) {
259259
isLeftGesture.value =
260-
startOffset.dx < MediaQuery.of(context).size.width / 2;
260+
startOffset.dx < MediaQuery.sizeOf(context).width / 2;
261261
isRightGesture.value = !isLeftGesture.value;
262262
}
263263

lib/hooks/use_keyboard.dart

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ KeyboardEvent useKeyboard({
3030

3131
final player = context.read<MediaPlayer>();
3232

33-
final isPlaying =
34-
context.select<MediaPlayer, bool>((player) => player.isPlaying);
35-
36-
final shuffle = useAppStore().state.shuffle;
37-
final isFullScreen = usePlayerUiStore().state.isFullScreen;
38-
final isShowControl = usePlayerUiStore().state.isShowControl;
39-
4033
void onKeyEvent(KeyEvent event) async {
4134
if (event.runtimeType == KeyDownEvent) {
4235
if (HardwareKeyboard.instance.isAltPressed) {
@@ -56,6 +49,7 @@ KeyboardEvent useKeyboard({
5649
}
5750

5851
if (HardwareKeyboard.instance.isControlPressed) {
52+
final appState = useAppStore().state;
5953
switch (event.logicalKey) {
6054
// 上一个
6155
case LogicalKeyboardKey.arrowLeft:
@@ -86,10 +80,12 @@ KeyboardEvent useKeyboard({
8680
// 随机
8781
case LogicalKeyboardKey.keyX:
8882
showControl();
89-
shuffle
90-
? usePlayQueueStore().sort()
91-
: usePlayQueueStore().shuffle();
92-
useAppStore().updateShuffle(!shuffle);
83+
if (appState.shuffle) {
84+
usePlayQueueStore().sort();
85+
} else {
86+
usePlayQueueStore().shuffle();
87+
}
88+
useAppStore().updateShuffle(!appState.shuffle);
9389
break;
9490
// 循环
9591
case LogicalKeyboardKey.keyR:
@@ -136,12 +132,13 @@ KeyboardEvent useKeyboard({
136132
return;
137133
}
138134

135+
final playerUiState = usePlayerUiStore().state;
139136
switch (event.logicalKey) {
140137
// 播放 | 暂停
141138
case LogicalKeyboardKey.space:
142139
case LogicalKeyboardKey.mediaPlayPause:
143140
showControl();
144-
if (isPlaying) {
141+
if (context.read<MediaPlayer>().isPlaying) {
145142
useAppStore().updateAutoPlay(false);
146143
player.pause();
147144
} else {
@@ -191,15 +188,15 @@ KeyboardEvent useKeyboard({
191188
break;
192189
// 退出全屏
193190
case LogicalKeyboardKey.escape:
194-
if (isDesktop && isFullScreen) {
191+
if (isDesktop && playerUiState.isFullScreen) {
195192
usePlayerUiStore().updateFullScreen(false);
196193
}
197194
break;
198195
// 全屏
199196
case LogicalKeyboardKey.enter:
200197
case LogicalKeyboardKey.f11:
201198
if (isDesktop) {
202-
usePlayerUiStore().updateFullScreen(!isFullScreen);
199+
usePlayerUiStore().updateFullScreen(!playerUiState.isFullScreen);
203200
}
204201
break;
205202
case LogicalKeyboardKey.tab:
@@ -229,7 +226,7 @@ KeyboardEvent useKeyboard({
229226
switch (event.logicalKey) {
230227
// 快退
231228
case LogicalKeyboardKey.arrowLeft:
232-
if (isShowControl) {
229+
if (usePlayerUiStore().state.isShowControl) {
233230
showControl();
234231
} else {
235232
showProgress();
@@ -238,7 +235,7 @@ KeyboardEvent useKeyboard({
238235
break;
239236
// 快进
240237
case LogicalKeyboardKey.arrowRight:
241-
if (isShowControl) {
238+
if (usePlayerUiStore().state.isShowControl) {
242239
showControl();
243240
} else {
244241
showProgress();

lib/hooks/use_volume.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ ValueNotifier<double?> useVolume(bool isGesture) {
1616
} catch (e) {
1717
logger('Error getting volume: $e');
1818
}
19-
return () => volume.value = null;
19+
return () {
20+
volume.value = null;
21+
FlutterVolumeController.updateShowSystemUI(true);
22+
};
2023
}, [isGesture]);
2124

2225
useEffect(() {

lib/l10n/iso_639.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
class Info {
22
final List<String> en;
33

4-
Info({required this.en});
4+
const Info({required this.en});
55
}
66

7-
Map<String, Info> customLanguageCodes = {
7+
const Map<String, Info> customLanguageCodes = {
88
'chs': Info(en: ['Chinese (Simplified)']),
99
'cht': Info(en: ['Chinese (Traditional)']),
1010
};
1111

12-
Map<String, Info> iso_639_1 = {
12+
const Map<String, Info> iso_639_1 = {
1313
'aa': Info(en: ['Afar']),
1414
'ab': Info(en: ['Abkhazian']),
1515
'ae': Info(en: ['Avestan']),
@@ -203,7 +203,7 @@ Map<String, Info> iso_639_1 = {
203203
'zu': Info(en: ['Zulu']),
204204
};
205205

206-
Map<String, Info> iso_639_2 = {
206+
const Map<String, Info> iso_639_2 = {
207207
'aar': Info(en: ['Afar']),
208208
'abk': Info(en: ['Abkhazian']),
209209
'ace': Info(en: ['Achinese']),

lib/l10n/languages.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
final Map<String, String> languages = {
1+
const Map<String, String> languages = {
22
'zh': '简体中文',
33
'en': 'English',
44
};

lib/main.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ void main(List<String> arguments) async {
6565
backgroundColor: Colors.transparent,
6666
skipTaskbar: false,
6767
titleBarStyle: TitleBarStyle.hidden,
68-
title: INFO.title,
6968
);
7069

7170
windowManager.waitUntilReadyToShow(windowOptions, () async {

lib/models/file.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ part 'file.freezed.dart';
66
part 'file.g.dart';
77

88
enum ContentType {
9-
dir,
109
video,
1110
audio,
1211
image,
@@ -38,7 +37,7 @@ abstract class FileItem with _$FileItem {
3837
factory FileItem.fromJson(Map<String, dynamic> json) =>
3938
_$FileItemFromJson(json);
4039

41-
String getID() => '$storageId:$uri}';
40+
String getID() => '$storageId:$uri';
4241
}
4342

4443
@freezed

lib/models/storages/ftp.dart

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import 'dart:convert';
2-
32
import 'package:iris/models/file.dart';
43
import 'package:iris/models/storages/storage.dart';
5-
import 'package:iris/utils/build_file_uri.dart';
64
import 'package:iris/utils/check_content_type.dart';
7-
import 'package:iris/utils/find_subtitle.dart';
5+
import 'package:iris/utils/get_subtitle_map.dart';
86
import 'package:iris/utils/logger.dart';
7+
import 'package:path/path.dart' as p;
98
import 'package:pure_ftp/pure_ftp.dart';
109

1110
Future<List<FileItem>> getFTPFiles(
@@ -36,30 +35,46 @@ Future<List<FileItem>> getFTPFiles(
3635
final baseUri =
3736
'ftp?host=${storage.host}&port=${storage.port}&path=${path.join('/').replaceAll('//', '/')}';
3837

39-
final allFileNames = files.map((file) => file.name).toList();
38+
String getUri(String fileName) {
39+
final separator = baseUri.endsWith('/') ? '' : '/';
40+
return Uri.encodeFull('$baseUri$separator$fileName');
41+
}
42+
43+
final subtitleMap = getSubtitleMap<FtpEntry>(
44+
files: files,
45+
baseUri: baseUri,
46+
getName: (file) => file.name,
47+
getUri: (file) => getUri(file.name),
48+
);
49+
50+
List<FileItem> fileItems = [];
4051

41-
return await Future.wait(files.map(
42-
(file) async => FileItem(
43-
storageId: storage.id,
44-
storageType: StorageType.ftp,
45-
name: file.name,
46-
uri: buildFileUri(baseUri, file.name),
47-
path: [...path, file.name],
48-
isDir: file.isDirectory,
49-
size: file.isDirectory ? 0 : file.info?.size ?? 0,
50-
lastModified: file.info?.modifyTime != null
51-
? DateTime.tryParse(file.info!.modifyTime!)
52-
: null,
53-
type: file.isDirectory ? ContentType.dir : checkContentType(file.name),
54-
subtitles: await findSubtitle(
55-
allFileNames,
56-
file.name,
57-
baseUri,
58-
),
59-
),
60-
));
52+
for (final file in files) {
53+
if (file.isDirectory || isMediaFile(file.name)) {
54+
final basename = p.basenameWithoutExtension(file.name).split('.').first;
55+
fileItems.add(
56+
FileItem(
57+
storageId: storage.id,
58+
storageType: StorageType.ftp,
59+
name: file.name,
60+
uri: getUri(file.name),
61+
path: [...path, file.name],
62+
isDir: file.isDirectory,
63+
size: file.isDirectory ? 0 : file.info?.size ?? 0,
64+
lastModified: file.info?.modifyTime != null
65+
? DateTime.tryParse(file.info!.modifyTime!)
66+
: null,
67+
type: checkContentType(file.name),
68+
subtitles:
69+
isVideoFile(file.name) ? subtitleMap[basename] ?? [] : [],
70+
),
71+
);
72+
}
73+
}
74+
75+
return fileItems;
6176
} catch (error) {
62-
logger('Error testing FTP: $error');
77+
logger('Error getting FTP files: $error');
6378
return [];
6479
}
6580
}

0 commit comments

Comments
 (0)