import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import '../../../core/utils/synchrodata.dart'; import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/meeting/meeting_task_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart'; import '../model/meeting_details_model.dart'; import '../model/meeting_model.dart'; class MeetingDetailsController extends GetxController with GetTickerProviderStateMixin { late TabController tabController; final RxInt currentIndex = 0.obs; late AnimationController lottieController; final _meetingUploadService = Get.find(); final _meetingTaskService = Get.find(); final PlayerController playerController = PlayerController(); StreamSubscription? _currentDurationSubscription; StreamSubscription? _playerStateSubscription; RxBool isTop = false.obs; RxInt durationMax = 0.obs; RxDouble dragOffset = 210.w.obs; RxBool isPlay = false.obs; RxInt currentDuration = 0.obs; RxInt speakerAllStarttime = (-1).obs; RxInt speakerAllEndtime = (-1).obs; int id = 0; RxBool isLoading = true.obs; Rx meetingData = MeetingModel().obs; Rx meetingDetails = MeetingDetailsModel().obs; RxList textList = [].obs; RxBool isSpeakerText = false.obs; List editSpeakerTextList = []; RxInt editSpeakerTextId = 0.obs; String editSpeakerTextChange = ''; int _everType = 99; RxInt summaryNumberModifications = 0.obs; RxInt personnelNumberModifications = 0.obs; String markmapLibJs = ''; String d3Js = ''; String markmapViewJs = ''; String markmapToolbarJs = ''; String saveSvgAsPngJs = ''; RxBool isDownload = false.obs; RxDouble downloadProgress = 0.0.obs; @override void onInit() { super.onInit(); tabController = TabController(length: 4, vsync: this); tabController.addListener(() { if (currentIndex.value == 2 && tabController.index != 2) { FocusScope.of(Get.context!).unfocus(); } currentIndex.value = tabController.index; }); lottieController = AnimationController(vsync: this) ..duration = const Duration(seconds: 2); final Map args = Get.arguments ?? {}; id = args['id']; _meetingTaskService.meetingId = id; _getDataDetails(); _loadJS(); ever(_meetingTaskService.tasktype, (value) { meetingDetails.value.tasktype = value; if (_everType <= value) { if (value == 2) { textList.value = _meetingTaskService.textList; } else if (value == 3) { meetingDetails.value.summary = _meetingTaskService.summary; meetingDetails.value.overview = _meetingTaskService.overview; summaryNumberModifications.value++; } } meetingDetails.refresh(); }); } @override void onClose() { _currentDurationSubscription?.cancel(); _playerStateSubscription?.cancel(); tabController.dispose(); playerController.dispose(); lottieController.dispose(); super.onClose(); } void popInvokedWithResult(didPop, result) async { _meetingUploadService.removeUpload(id); _meetingTaskService.removeTask(id); _meetingTaskService.meetingId = 0; } void _getDataDetails() async { final data = await SqfliteApi.getMeetingDetails(id); meetingData.value = MeetingModel.fromJson(data); meetingDetails.value = MeetingDetailsModel.fromJson(data['details']); _meetingTaskService.tasktype.value = meetingDetails.value.tasktype; if (meetingDetails.value.tasktype >= 3) { dragOffset.value = 0; isTop.value = true; } if (meetingData.value.filepath.isNotEmpty) { await _loadPlayerController(); } if (meetingDetails.value.tasktype >= 2) { List speakerList = await SqfliteApi.getMeetingSpeaker(id); textList.value = List.from(speakerList); } isLoading.value = false; } Future _loadPlayerController() async { await playerController.preparePlayer( path: meetingData.value.filepath, shouldExtractWaveform: false, noOfSamples: 88, ); durationMax.value = await playerController.getDuration(DurationType.max); playerController.setFinishMode(finishMode: FinishMode.pause); _currentDurationSubscription = playerController.onCurrentDurationChanged.listen((int position) { currentDuration.value = position; if (speakerAllEndtime.value > 0) { if (position >= speakerAllEndtime.value) { playerController.pausePlayer(); speakerAllStarttime.value = -1; speakerAllEndtime.value = -1; } } }); _playerStateSubscription = playerController.onPlayerStateChanged.listen((PlayerState playerState) { isPlay.value = playerState.isPlaying; if (playerState.isPlaying) { lottieController.repeat(); } else { lottieController.stop(); } }); } void _loadJS() async { markmapLibJs = await rootBundle.loadString('assets/js/markmap-lib.js'); d3Js = await rootBundle.loadString('assets/js/d3@7.js'); markmapViewJs = await rootBundle.loadString('assets/js/markmap-view.js'); markmapToolbarJs = await rootBundle.loadString('assets/js/markmap-toolbar.js'); saveSvgAsPngJs = await rootBundle.loadString('assets/js/saveSvgAsPng.min.js'); } void downloadAudio() async { if (isDownload.value) return; isDownload.value = true; final directory = await getApplicationDocumentsDirectory(); final directoryDir = Directory('${directory.path}/DownloadAudio'); if (!await directoryDir.exists()) { await directoryDir.create(recursive: true); } final fileName = meetingData.value.audiourl.split('/').last; final String filePath = '${directory.path}/DownloadAudio/$fileName'; await Dio().download( meetingData.value.audiourl, filePath, onReceiveProgress: (int received, int total) { downloadProgress.value = total > 0 ? received / total : 0.0; }, ); meetingData.value.filepath = filePath; meetingData.refresh(); _loadPlayerController(); SqfliteApi.editMeetingTitle(id, {'filePath': filePath}); isDownload.value = false; } void editSpeakerText() async { if (editSpeakerTextId.value != 0 && editSpeakerTextChange.isNotEmpty) { Map? foundElement = editSpeakerTextList.firstWhere( (element) => element['id'] == editSpeakerTextId.value, orElse: () => null, ); if (foundElement != null) { foundElement['content'] = editSpeakerTextChange; } else { editSpeakerTextList.add({ 'id': editSpeakerTextId.value, 'content': editSpeakerTextChange, }); } } if (editSpeakerTextList.isNotEmpty) { EasyLoading.show(); var result = await SqfliteApi.editMeetingSpeaker(editSpeakerTextList); if (result != null) { for (var element in editSpeakerTextList) { int index = textList.indexWhere((item) => item['id'] == element['id']); if (index != -1) { final dataMap = Map.from(textList[index]); dataMap['content'] = element['content']; textList[index] = dataMap; } } Synchrodata.saveOperationRecord('update', { 'original': jsonEncode(textList), 'id': id, }); isSpeakerText.value = false; editSpeakerTextId.value = 0; editSpeakerTextList = []; } EasyLoading.dismiss(); } else { isSpeakerText.value = false; editSpeakerTextId.value = 0; editSpeakerTextList = []; } } void summaryCreate() async { EasyLoading.show(); _everType = 2; String audiourl = meetingData.value.audiourl; String taskId = meetingDetails.value.taskid; await _meetingTaskService.addTask(id, audiourl, taskId); meetingDetails.value.tasktype = 1; meetingDetails.refresh(); EasyLoading.dismiss(); } void refreshSummary() async { String text = ''; for (var element in textList) { if (element['speaker'].isNotEmpty) { text += '[${element['speaker']}]: '; } text += '${element['content']}\n'; } _everType = 3; await SqfliteApi.editMeeting(id, {'tasktype': 2}); _meetingTaskService.tasktype.value = 2; _meetingTaskService.aiServiceMessage(id, text); } }