You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

428 lines
12 KiB

import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../core/utils/logger.dart';
import '../../../core/utils/synchrodata.dart';
import '../../../data/services/ble_manager.dart';
import '../../../data/services/db/sqflite_api.dart';
import '../../../core/utils/permission_util.dart';
import 'package:share_plus/share_plus.dart';
import '../../../data/services/meeting/meeting_task_service.dart';
import '../../../data/services/meeting/meeting_upload_service.dart';
import '../../../routes/app_routes.dart';
import 'meeting_controller.dart';
class MeetingHomeController extends GetxController {
final meetingController = Get.find<MeetingController>();
final meetingUploadService = Get.find<MeetingUploadService>();
final meetingTaskService = Get.find<MeetingTaskService>();
final AudioPlayer _audioPlayer = AudioPlayer();
List<String> selected = [];
final appDocDir = getApplicationDocumentsDirectory();
List<FileSystemEntity> files = [];
TextEditingController titleController = TextEditingController(); // 录音标题
String titleCSuffix = ""; // 录音标题后缀
RxList originalDataList = [].obs;
RxList dataList = [].obs;
RxString filtrateType = ''.obs;
bool isAsc = false;
TextEditingController filtrateController = TextEditingController();
// 蓝牙服务
final bleManager = Get.find<BleManager>();
@override
void onInit() {
super.onInit();
_requestStoragePermission();
_initGetDataList();
addMeetingAudio();
final Map<String, dynamic> args = Get.arguments ?? {};
// 检查参数是否存在且包含有效路径
if (args.isNotEmpty &&
args.containsKey('filePath') &&
args['filePath'] is String &&
args['filePath'].isNotEmpty) {
final String path = args['filePath'];
addAudio(path);
}
}
@override
void onClose() {
titleController.dispose();
filtrateController.dispose();
_audioPlayer.dispose();
super.onClose();
}
void onPopInvokedWithResult(didPop, result) {
Synchrodata.uploadOperationRecords(isSynchro: true);
}
void _initGetDataList() async {
await Synchrodata.uploadOperationRecords(isSynchro: true);
_getDataList();
}
// 请求本地数据
void _getDataList() async {
List list = await SqfliteApi.getMeetingList();
originalDataList.value = List.from(list);
dataList.value = List.from(list);
}
// 请求存储权限
void _requestStoragePermission() async {
PermissionUtil.instance
.requestPermission(
permissionType: Permission.storage,
permissionName: "存储",
explanationText: "我们需要访问存储权限,以便您可以选择并上传音频文件。",
permanentDenialText: "检测到您已永久拒绝存储权限。如需继续使用此功能,请前往应用设置中手动开启存储权限。",
)
.then((granted) {
if (granted) {
Logger.d("Permission", "用户授予了存储权限");
} else {
Logger.d("Permission", "用户未授予存储权限");
}
});
}
void loadTranslatopus() async {
final dir = Directory("/data/user/0/com.saitong.voitrans/files/OpusAudio");
files = dir
.listSync()
.where((f) => f is File && (f.path.endsWith('.opus')))
.toList();
}
void loadTranslatAudio() async {
try {
final appDir = await appDocDir; // 先 await 获取目录
final dir = Directory("${appDir.path}/TranslatAudio");
if (!await dir.exists()) {
await dir.create(recursive: true);
files = [];
return;
}
files = dir
.listSync()
.where((f) =>
f is File &&
(f.path.endsWith('.mp3') ||
f.path.endsWith('.wav') ||
f.path.endsWith('.aac') ||
f.path.endsWith('.m4a') ||
f.path.endsWith('.mp4') ||
f.path.endsWith('.mov')))
.toList();
} catch (e) {
files = [];
}
}
Future<void> shareAllTranslatAudio() async {
final filePath = selected[0];
try {
await Share.shareXFiles(
[XFile(filePath)],
text: '分享会议音频',
);
EasyLoading.showToast('分享完成!');
} catch (e) {
EasyLoading.showToast('分享失败: ${e.toString()}');
}
}
void addMeetingAudio() async {
EasyLoading.show(status: '正在导入本地录音...');
try {
int addUp = 0;
final appDir = await appDocDir;
final sourceDirs = [
Directory("${appDir.path}/MeetingAudio/"),
Directory("${appDir.path}/DestAudio/"),
];
final destDir = Directory("${appDir.path}/LocalAudio/");
if (!await destDir.exists()) {
await destDir.create(recursive: true);
}
List<FileSystemEntity> allFiles = [];
for (final dir in sourceDirs) {
if (await dir.exists()) {
final files =
await dir.list().where((entity) => entity is File).toList();
allFiles.addAll(files);
}
}
for (final fileEntity in allFiles) {
try {
final file = File(fileEntity.path);
final filePath = file.path;
// 设置音频源获取时长
await _audioPlayer.setAudioSource(AudioSource.file(filePath));
int seconds = _audioPlayer.duration?.inSeconds ?? 0;
// 获取文件修改时间
final fileStat = await file.stat();
final creationTime = fileStat.changed.millisecondsSinceEpoch;
// 准备新路径
final fileName = filePath.split('/').last;
final newPath = "${destDir.path}/$fileName";
// 构建数据
Map<String, Object?> data = {
'title': fileName,
'type': 'LOCAL',
'seconds': seconds,
'filepath': newPath, // 存储新路径
'audiourl': '',
'tasktype': 0,
'creationtime': creationTime,
};
// 插入数据库
final meetingId = await SqfliteApi.insertMeeting(data);
if (meetingId != null) {
// 上传OSS
meetingUploadService.addUpload(meetingId, newPath, 'LocalAudio');
originalDataList.insert(0, {
'id': meetingId,
...data,
});
// 更新UI数据
if (isAsc) {
dataList.add({
'id': meetingId,
...data,
});
} else {
dataList.insert(0, {
'id': meetingId,
...data,
});
}
// 移动文件
await file.copy(newPath);
await file.delete();
addUp++;
}
} catch (e) {
print("文件处理失败: ${fileEntity.path}, 错误: $e");
}
}
if (addUp > 0) EasyLoading.showSuccess('成功导入本地 $addUp 条录音');
} catch (e) {
EasyLoading.showError('导入失败: $e');
} finally {
EasyLoading.dismiss();
}
}
void addTranslatAudio() async {
EasyLoading.show(status: '正在批量导入...');
for (final filePath in selected) {
try {
await _audioPlayer.setAudioSource(AudioSource.file(filePath));
final fileStat = await File(filePath).stat();
final creationTime = fileStat.changed.millisecondsSinceEpoch; // 时间戳
int seconds = _audioPlayer.duration?.inSeconds ?? 0;
final fileName = filePath.split('/').last;
Map<String, Object?> data = {
'title': fileName,
'type': 'TRANSLAT',
'seconds': seconds,
'filepath': filePath,
'audiourl': '',
'tasktype': 0,
'creationtime': creationTime,
};
final meetingId = await SqfliteApi.insertMeeting(data);
if (meetingId != null) {
meetingUploadService.addUpload(meetingId, filePath, 'TranslatAudio');
originalDataList.insert(0, {
'id': meetingId,
...data,
});
if (isAsc) {
dataList.add({
'id': meetingId,
...data,
});
} else {
dataList.insert(0, {
'id': meetingId,
...data,
});
}
}
} catch (e) {
Logger.error('导入文件失败: $filePath, error: $e');
}
}
EasyLoading.dismiss();
}
void addAudio([String? filePath]) async {
EasyLoading.show(status: '正在导入...');
String fileName = "";
bool isParamProvided = filePath != null; // 更清晰的变量名
if (filePath != null) {
// 正确截取文件名(跨平台安全方式)
fileName = path.basename(filePath);
} else {
FilePickerResult? result = await FilePicker.platform.pickFiles(
withData: true,
type: FileType.audio,
);
if (result != null) {
PlatformFile file = result.files.first;
filePath = await _saveFile(file);
fileName = file.name;
} else {
EasyLoading.dismiss();
return;
}
}
final meetingId = await insertMeetingAudio(fileName, filePath);
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
}
Future insertMeetingAudio(
String fileName,
String filePath, {
bool isUpload = true,
}) async {
await _audioPlayer.setAudioSource(AudioSource.file(filePath));
int seconds = _audioPlayer.duration?.inSeconds ?? 0;
int creationTime = DateTime.now().millisecondsSinceEpoch;
Map<String, Object?> data = {
'title': fileName,
'type': 'IMPORT',
'seconds': seconds,
'filepath': filePath,
'audiourl': '',
'tasktype': 0,
'creationtime': creationTime,
};
final meetingId = await SqfliteApi.insertMeeting(data);
if (meetingId != null) {
meetingUploadService.addUpload(
meetingId,
filePath,
'ExternalAudio',
isUpload: isUpload,
);
originalDataList.insert(0, {
'id': meetingId,
...data,
});
if (isAsc) {
dataList.add({
'id': meetingId,
...data,
});
} else {
dataList.insert(0, {
'id': meetingId,
...data,
});
}
}
return meetingId;
}
Future<String> _saveFile(PlatformFile file) async {
final directory = await getApplicationDocumentsDirectory();
final directoryDir = Directory('${directory.path}/ExternalAudio');
if (!await directoryDir.exists()) {
await directoryDir.create(recursive: true);
}
final String filePath = '${directory.path}/ExternalAudio/${file.name}';
final File localFile = File(filePath);
await localFile.writeAsBytes(file.bytes!);
return filePath;
}
// 修改标题
void editMeetingTitle(int id) async {
if (titleController.text.isNotEmpty) {
String title = titleController.text + titleCSuffix;
SqfliteApi.editMeetingTitle(id, {'title': title});
int originalIndex =
originalDataList.indexWhere((item) => item['id'] == id);
int index = dataList.indexWhere((item) => item['id'] == id);
if (originalIndex != -1) {
final dataMap =
Map<String, dynamic>.from(originalDataList[originalIndex]);
dataMap['title'] = title;
originalDataList[originalIndex] = dataMap;
}
if (index != -1) {
final dataMap = Map<String, dynamic>.from(dataList[index]);
dataMap['title'] = title;
dataList[index] = dataMap;
}
}
Get.back();
}
// 删除数据
void deleteMeeting(int id, String filepath) async {
originalDataList.removeWhere((item) => item['id'] == id);
dataList.removeWhere((item) => item['id'] == id);
// 删除数据库中的记录
SqfliteApi.deleteMeeting(id);
_deleteFile(filepath);
meetingUploadService.removeUpload(id, isDeleteTask: true);
meetingTaskService.removeTask(id, isDeleteTask: true);
}
// 删除文件
Future<void> _deleteFile(String filepath) async {
File file = File(filepath);
bool fileExists = await file.exists();
if (fileExists) {
await file.delete();
}
}
}