|
|
@ -41,7 +41,7 @@ class MeetingHomeController extends GetxController { |
|
|
RxList originalDataList = [].obs; |
|
|
RxList originalDataList = [].obs; |
|
|
RxList dataList = [].obs; |
|
|
RxList dataList = [].obs; |
|
|
RxString filtrateType = ''.obs; |
|
|
RxString filtrateType = ''.obs; |
|
|
bool isAsc = false; |
|
|
bool isAsc = false; // 是否升序排序 |
|
|
TextEditingController filtrateController = TextEditingController(); |
|
|
TextEditingController filtrateController = TextEditingController(); |
|
|
|
|
|
|
|
|
// 蓝牙服务 |
|
|
// 蓝牙服务 |
|
|
@ -178,15 +178,16 @@ class MeetingHomeController extends GetxController { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 核心方法:导入本地录音文件到统一目录 |
|
|
void addMeetingAudio() async { |
|
|
void addMeetingAudio() async { |
|
|
EasyLoading.show(status: 'importingLocalRecordings'.tr); // 对应中文:正在导入本地录音... |
|
|
EasyLoading.show(status: 'importingLocalRecordings'.tr); // 对应中文:正在导入本地录音... |
|
|
try { |
|
|
try { |
|
|
int addUp = 0; |
|
|
int addUp = 0; // 新增计数 |
|
|
final appDir = await appDocDir; |
|
|
final appDir = await appDocDir; // 获取应用文档目录 |
|
|
|
|
|
|
|
|
final sourceDirs = [ |
|
|
final sourceDirs = [ |
|
|
Directory("${appDir.path}/MeetingAudio/"), |
|
|
Directory("${appDir.path}/MeetingAudio/"), // 会议录音目录 |
|
|
Directory("${appDir.path}/DestAudio/"), |
|
|
Directory("${appDir.path}/DestAudio/"), // 目标录音目录 |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
// 目标目录:LocalAudio |
|
|
// 目标目录:LocalAudio |
|
|
@ -200,31 +201,33 @@ class MeetingHomeController extends GetxController { |
|
|
|
|
|
|
|
|
for (final dir in sourceDirs) { |
|
|
for (final dir in sourceDirs) { |
|
|
if (await dir.exists()) { |
|
|
if (await dir.exists()) { |
|
|
|
|
|
//遍历每个源目录,筛选出文件实体(仅包含文件,不包含子目录) |
|
|
final files = |
|
|
final files = |
|
|
await dir.list().where((entity) => entity is File).toList(); |
|
|
await dir.list().where((entity) => entity is File).toList(); |
|
|
allFiles.addAll(files); |
|
|
allFiles.addAll(files); //合并到总文件列表 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有文件实体 |
|
|
for (final fileEntity in allFiles) { |
|
|
for (final fileEntity in allFiles) { |
|
|
try { |
|
|
try { |
|
|
final file = File(fileEntity.path); |
|
|
final file = File(fileEntity.path); //将文件实体转换为具体文件操作对象 |
|
|
final filePath = file.path; |
|
|
final filePath = file.path; |
|
|
|
|
|
|
|
|
// 设置音频源获取时长 |
|
|
// 使用just_audio库加载音频文件以获取时长 |
|
|
|
|
|
|
|
|
await _audioPlayer.setAudioSource(AudioSource.file(filePath)); |
|
|
await _audioPlayer.setAudioSource(AudioSource.file(filePath)); |
|
|
int seconds = _audioPlayer.duration?.inSeconds ?? 0; |
|
|
int seconds = |
|
|
|
|
|
_audioPlayer.duration?.inSeconds ?? 0; //获取音频时长(秒),若为空则设为0 |
|
|
|
|
|
|
|
|
// 获取文件修改时间 |
|
|
// 获取文件修改时间 |
|
|
final fileStat = await file.stat(); |
|
|
final fileStat = await file.stat(); //获取文件状态信息,包含修改时间等 |
|
|
final creationTime = fileStat.changed.millisecondsSinceEpoch; |
|
|
final creationTime = fileStat.changed.millisecondsSinceEpoch; |
|
|
|
|
|
|
|
|
// 准备新路径 |
|
|
// 准备新路径 |
|
|
final fileName = filePath.split('/').last; |
|
|
final fileName = filePath.split('/').last; //获取文件名(包含扩展名) |
|
|
final newPath = "${destDir.path}/$fileName"; |
|
|
final newPath = "${destDir.path}/$fileName"; //构建新路径,保持原文件名 |
|
|
|
|
|
|
|
|
// 构建数据 |
|
|
// 构建要存入数据库的音频文件抽象对象 |
|
|
Map<String, Object?> data = { |
|
|
Map<String, Object?> data = { |
|
|
'title': fileName, |
|
|
'title': fileName, |
|
|
'type': 'LOCAL', |
|
|
'type': 'LOCAL', |
|
|
@ -238,7 +241,7 @@ class MeetingHomeController extends GetxController { |
|
|
// 插入数据库 |
|
|
// 插入数据库 |
|
|
final meetingId = await SqfliteApi.insertMeeting(data); |
|
|
final meetingId = await SqfliteApi.insertMeeting(data); |
|
|
if (meetingId != null) { |
|
|
if (meetingId != null) { |
|
|
// 上传OSS |
|
|
// 安排后台上传任务 |
|
|
meetingUploadService.addUpload(meetingId, newPath, 'LocalAudio'); |
|
|
meetingUploadService.addUpload(meetingId, newPath, 'LocalAudio'); |
|
|
|
|
|
|
|
|
originalDataList.insert(0, { |
|
|
originalDataList.insert(0, { |
|
|
@ -259,8 +262,8 @@ class MeetingHomeController extends GetxController { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 移动文件 |
|
|
// 移动文件 |
|
|
await file.copy(newPath); |
|
|
await file.copy(newPath); // 复制到新路径 |
|
|
await file.delete(); |
|
|
await file.delete(); // 删除原文件 |
|
|
|
|
|
|
|
|
addUp++; |
|
|
addUp++; |
|
|
} |
|
|
} |
|
|
@ -272,10 +275,11 @@ class MeetingHomeController extends GetxController { |
|
|
e.toString())); // 对应中文:文件处理失败: {filePath}, 错误: {error} |
|
|
e.toString())); // 对应中文:文件处理失败: {filePath}, 错误: {error} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (addUp > 0) |
|
|
if (addUp > 0) { |
|
|
EasyLoading.showSuccess('importSuccess' |
|
|
EasyLoading.showSuccess('importSuccess' |
|
|
.tr |
|
|
.tr |
|
|
.replaceAll('{count}', '$addUp')); // 对应中文:成功导入本地 {count} 条录音 |
|
|
.replaceAll('{count}', '$addUp')); // 对应中文:成功导入本地 {count} 条录音 |
|
|
|
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
EasyLoading.showError('importError' |
|
|
EasyLoading.showError('importError' |
|
|
.tr |
|
|
.tr |
|
|
@ -285,50 +289,88 @@ class MeetingHomeController extends GetxController { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void addTranslatAudio() async { |
|
|
// 新增:将翻译音频归档到 LocalAudio 并返回新路径(优先 rename,失败再 copy+delete) |
|
|
|
|
|
Future<String> _archiveTranslatToLocal(String srcPath) async { |
|
|
|
|
|
// 中文注释:获取应用文档目录并确保 LocalAudio 目录存在 |
|
|
|
|
|
final appDir = await appDocDir; |
|
|
|
|
|
final destDir = Directory(path.join(appDir.path, 'LocalAudio')); |
|
|
|
|
|
if (!await destDir.exists()) { |
|
|
|
|
|
await destDir.create(recursive: true); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 中文注释:目标文件名与路径,处理重名冲突 |
|
|
|
|
|
final fileName = path.basename(srcPath); |
|
|
|
|
|
final baseName = path.basenameWithoutExtension(fileName); |
|
|
|
|
|
final ext = path.extension(fileName); |
|
|
|
|
|
String destPath = path.join(destDir.path, fileName); |
|
|
|
|
|
|
|
|
|
|
|
if (await File(destPath).exists()) { |
|
|
|
|
|
final ts = DateTime.now().millisecondsSinceEpoch; |
|
|
|
|
|
destPath = path.join(destDir.path, '$baseName\_$ts$ext'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 中文注释:尝试 rename(更快),失败则 copy + delete |
|
|
|
|
|
final srcFile = File(srcPath); |
|
|
|
|
|
try { |
|
|
|
|
|
await srcFile.rename(destPath); |
|
|
|
|
|
return destPath; |
|
|
|
|
|
} catch (_) { |
|
|
|
|
|
await srcFile.copy(destPath); |
|
|
|
|
|
await srcFile.delete(); |
|
|
|
|
|
return destPath; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 修改:翻译音频导入支持归档到 LocalAudio(智能转写默认归档以隐藏原翻译目录) |
|
|
|
|
|
void addTranslatAudio({bool archiveToLocal = true}) async { |
|
|
EasyLoading.show(status: 'batchImporting'.tr); // 对应中文:正在批量导入... |
|
|
EasyLoading.show(status: 'batchImporting'.tr); // 对应中文:正在批量导入... |
|
|
for (final filePath in selected) { |
|
|
for (final originalPath in selected) { |
|
|
try { |
|
|
try { |
|
|
await _audioPlayer.setAudioSource(AudioSource.file(filePath)); |
|
|
// 中文注释:根据开关决定是否将翻译音频迁移到 LocalAudio |
|
|
final fileStat = await File(filePath).stat(); |
|
|
final usePath = |
|
|
final creationTime = fileStat.changed.millisecondsSinceEpoch; // 时间戳 |
|
|
archiveToLocal ? await _archiveTranslatToLocal(originalPath) : originalPath; |
|
|
int seconds = _audioPlayer.duration?.inSeconds ?? 0; |
|
|
|
|
|
final fileName = filePath.split('/').last; |
|
|
// 中文注释:计算音频时长与创建时间 |
|
|
Map<String, Object?> data = { |
|
|
await _audioPlayer.setAudioSource(AudioSource.file(usePath)); |
|
|
|
|
|
final fileStat = await File(usePath).stat(); |
|
|
|
|
|
final creationTime = fileStat.changed.millisecondsSinceEpoch; |
|
|
|
|
|
final seconds = _audioPlayer.duration?.inSeconds ?? 0; |
|
|
|
|
|
final fileName = path.basename(usePath); |
|
|
|
|
|
|
|
|
|
|
|
// 中文注释:归档则统一标记为 LOCAL,未归档则保留 TRANSLAT |
|
|
|
|
|
final String recordType = archiveToLocal ? 'LOCAL' : 'TRANSLAT'; |
|
|
|
|
|
|
|
|
|
|
|
// 中文注释:写入数据库 |
|
|
|
|
|
final Map<String, Object?> data = { |
|
|
'title': fileName, |
|
|
'title': fileName, |
|
|
'type': 'TRANSLAT', |
|
|
'type': recordType, |
|
|
'seconds': seconds, |
|
|
'seconds': seconds, |
|
|
'filepath': filePath, |
|
|
'filepath': usePath, |
|
|
'audiourl': '', |
|
|
'audiourl': '', |
|
|
'tasktype': 0, |
|
|
'tasktype': 0, |
|
|
'creationtime': creationTime, |
|
|
'creationtime': creationTime, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
final meetingId = await SqfliteApi.insertMeeting(data); |
|
|
final meetingId = await SqfliteApi.insertMeeting(data); |
|
|
if (meetingId != null) { |
|
|
if (meetingId != null) { |
|
|
meetingUploadService.addUpload(meetingId, filePath, 'TranslatAudio'); |
|
|
// 中文注释:上传分类按目录区分 |
|
|
|
|
|
final uploadBucket = archiveToLocal ? 'LocalAudio' : 'TranslatAudio'; |
|
|
|
|
|
meetingUploadService.addUpload(meetingId, usePath, uploadBucket); |
|
|
|
|
|
|
|
|
originalDataList.insert(0, { |
|
|
// 中文注释:更新列表(保持现有排序逻辑) |
|
|
'id': meetingId, |
|
|
final record = {'id': meetingId, ...data}; |
|
|
...data, |
|
|
originalDataList.insert(0, record); |
|
|
}); |
|
|
|
|
|
if (isAsc) { |
|
|
if (isAsc) { |
|
|
dataList.add({ |
|
|
dataList.add(record); |
|
|
'id': meetingId, |
|
|
|
|
|
...data, |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
dataList.insert(0, { |
|
|
dataList.insert(0, record); |
|
|
'id': meetingId, |
|
|
|
|
|
...data, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('importFailed' |
|
|
Logger.error('importFailed' |
|
|
.tr |
|
|
.tr |
|
|
.replaceAll('{filePath}', filePath) |
|
|
.replaceAll('{filePath}', originalPath) |
|
|
.replaceAll('{error}', |
|
|
.replaceAll('{error}', e.toString())); // 对应中文:导入文件失败: {filePath}, error: {error} |
|
|
e.toString())); // 对应中文:导入文件失败: {filePath}, error: {error} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
EasyLoading.dismiss(); |
|
|
EasyLoading.dismiss(); |
|
|
|