diff --git a/lib/common/widgets/dialog/report.dart b/lib/common/widgets/dialog/report.dart index 8f2504d18..56d10a54b 100644 --- a/lib/common/widgets/dialog/report.dart +++ b/lib/common/widgets/dialog/report.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/common/widgets/radio_widget.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:flutter/foundation.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -127,10 +127,10 @@ Future autoWrapReportDialog( } else { SmartDialog.showToast(data['message'].toString()); } - } catch (e) { + } catch (e, s) { SmartDialog.dismiss(); SmartDialog.showToast('提交失败:$e'); - if (kDebugMode) rethrow; + Utils.reportError(e, s); } }, child: const Text('确定'), diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 8312a8472..590af8e3a 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/grpc/dm.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/path_utils.dart'; -import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:PiliPlus/utils/utils.dart'; import 'package:path/path.dart' as path; class PlDanmakuController { @@ -121,6 +121,7 @@ class PlDanmakuController { _initFileDm(); } + @pragma('vm:notify-debugger-on-exception') Future _initFileDm() async { try { final file = File( @@ -131,8 +132,8 @@ class PlDanmakuController { if (bytes.isEmpty) return; final elem = DmSegMobileReply.fromBuffer(bytes).elems; handleDanmaku(elem); - } catch (_) { - if (kDebugMode) rethrow; + } catch (e, s) { + Utils.reportError(e, s); } } } diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index fb33e6c31..415c202ed 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -123,6 +123,7 @@ class _EpisodePanelState extends State return; } + @pragma('vm:notify-debugger-on-exception') void jumpToCurrent() { final newItemIndex = _findCurrentItemIndex; if (_currentItemIndex != newItemIndex) { @@ -131,8 +132,8 @@ class _EpisodePanelState extends State _itemScrollController[_currentTabIndex.value].jumpTo( _calcItemOffset(newItemIndex), ); - } catch (_) { - if (kDebugMode) rethrow; + } catch (e, s) { + Utils.reportError(e, s); } } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 82948d39a..02375ea8f 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -333,83 +333,85 @@ class LiveRoomController extends GetxController { .map((host) => 'wss://${host.host}:${host.wssPort}/sub') .toList(), ) - ..addEventListener((obj) { - try { - // logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}'); - switch (obj['cmd']) { - case 'DANMU_MSG': - final info = obj['info']; - final first = info[0]; - final content = first[15]; - final Map extra = jsonDecode( - content['extra'], - ); - final user = content['user']; - // final midHash = first[7]; - final uid = user['uid']; - final name = user['base']['name']; - final msg = info[1]; - BaseEmote? uemote; - if (first[13] case Map map) { - uemote = BaseEmote.fromJson(map); - } - messages.add( - DanmakuMsg( - name: name, - uid: uid, - text: msg, - emots: (extra['emots'] as Map?)?.map( - (k, v) => MapEntry(k, BaseEmote.fromJson(v)), - ), - uemote: uemote, - ), - ); - - if (plPlayerController.showDanmaku) { - final checkInfo = info[9]; - danmakuController?.addDanmaku( - DanmakuContentItem( - msg, - color: plPlayerController.blockColorful - ? Colors.white - : DmUtils.decimalToColor(extra['color']), - type: DmUtils.getPosition(extra['mode']), - selfSend: extra['send_from_me'] ?? false, - extra: LiveDanmaku( - id: extra['id_str'], - mid: uid, - dmType: extra['dm_type'], - ts: checkInfo['ts'], - ct: checkInfo['ct'], - ), - ), - ); - if (!disableAutoScroll.value) { - EasyThrottle.throttle( - 'liveDm', - const Duration(milliseconds: 500), - () => WidgetsBinding.instance.addPostFrameCallback( - scrollToBottom, - ), - ); - } - } - break; - case 'SUPER_CHAT_MESSAGE' when showSuperChat: - final item = SuperChatItem.fromJson(obj['data']); - superChatMsg.insert(0, item); - if (isFullScreen || plPlayerController.isDesktopPip) { - fsSC.value = item; - } - break; - } - } catch (_) { - if (kDebugMode) rethrow; - } - }) + ..addEventListener(_danmakuListener) ..init(); } + void _danmakuListener(dynamic obj) { + try { + // logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}'); + switch (obj['cmd']) { + case 'DANMU_MSG': + final info = obj['info']; + final first = info[0]; + final content = first[15]; + final Map extra = jsonDecode( + content['extra'], + ); + final user = content['user']; + // final midHash = first[7]; + final uid = user['uid']; + final name = user['base']['name']; + final msg = info[1]; + BaseEmote? uemote; + if (first[13] case Map map) { + uemote = BaseEmote.fromJson(map); + } + messages.add( + DanmakuMsg( + name: name, + uid: uid, + text: msg, + emots: (extra['emots'] as Map?)?.map( + (k, v) => MapEntry(k, BaseEmote.fromJson(v)), + ), + uemote: uemote, + ), + ); + + if (plPlayerController.showDanmaku) { + final checkInfo = info[9]; + danmakuController?.addDanmaku( + DanmakuContentItem( + msg, + color: plPlayerController.blockColorful + ? Colors.white + : DmUtils.decimalToColor(extra['color']), + type: DmUtils.getPosition(extra['mode']), + selfSend: extra['send_from_me'] ?? false, + extra: LiveDanmaku( + id: extra['id_str'], + mid: uid, + dmType: extra['dm_type'], + ts: checkInfo['ts'], + ct: checkInfo['ct'], + ), + ), + ); + if (!disableAutoScroll.value) { + EasyThrottle.throttle( + 'liveDm', + const Duration(milliseconds: 500), + () => WidgetsBinding.instance.addPostFrameCallback( + scrollToBottom, + ), + ); + } + } + break; + case 'SUPER_CHAT_MESSAGE' when showSuperChat: + final item = SuperChatItem.fromJson(obj['data']); + superChatMsg.insert(0, item); + if (isFullScreen || plPlayerController.isDesktopPip) { + fsSC.value = item; + } + break; + } + } catch (_) { + if (kDebugMode) rethrow; + } + } + final RxInt likeClickTime = 0.obs; Timer? likeClickTimer; diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index 864c7fef3..42fcb93f6 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -1815,6 +1815,7 @@ class VideoDetailController extends GetxController } } + @pragma('vm:notify-debugger-on-exception') bool onSkipSegment() { try { if (plPlayerController.enableSponsorBlock) { @@ -1824,8 +1825,8 @@ class VideoDetailController extends GetxController return true; } } - } catch (_) { - if (kDebugMode) rethrow; + } catch (e, s) { + Utils.reportError(e, s); } return false; } diff --git a/lib/pages/video/download_panel/view.dart b/lib/pages/video/download_panel/view.dart index 5e7b83ea3..abcb8b740 100644 --- a/lib/pages/video/download_panel/view.dart +++ b/lib/pages/video/download_panel/view.dart @@ -20,6 +20,7 @@ import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode, kReleaseMode; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -284,8 +285,8 @@ class _DownloadPanelState extends State { } cidSet.add(cid); return true; - } catch (e) { - if (kDebugMode) rethrow; + } catch (e, s) { + Utils.reportError(e, s); SmartDialog.showToast(e.toString()); } return false; diff --git a/lib/pages/video/reply_reply/controller.dart b/lib/pages/video/reply_reply/controller.dart index 107e55ea6..c79c2ec45 100644 --- a/lib/pages/video/reply_reply/controller.dart +++ b/lib/pages/video/reply_reply/controller.dart @@ -7,9 +7,9 @@ import 'package:PiliPlus/pages/video/reply_new/view.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:fixnum/fixnum.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 453492d22..db27b583b 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -1150,8 +1150,8 @@ class HeaderControlState extends State { allowedExtensions: const ['json'], ); } - } catch (e) { - if (kDebugMode) rethrow; + } catch (e, s) { + Utils.reportError(e, s); SmartDialog.showToast(e.toString()); } }, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index ea98a5ca4..9dd1ee086 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -830,6 +830,7 @@ class PlPlayerController { bufferSize: Pref.expandBuffer ? (isLive ? 64 * 1024 * 1024 : 32 * 1024 * 1024) : (isLive ? 16 * 1024 * 1024 : 4 * 1024 * 1024), + logLevel: kDebugMode ? MPVLogLevel.warn : MPVLogLevel.error, ), ); final pp = player.platform!; @@ -888,8 +889,7 @@ class PlPlayerController { final Map? filters; if (Platform.isAndroid) { - String audioNormalization = ''; - audioNormalization = AudioNormalization.getParamFromConfig( + String audioNormalization = AudioNormalization.getParamFromConfig( Pref.audioNormalization, ); if (volume != null && volume.isNotEmpty) { @@ -1109,12 +1109,13 @@ class PlPlayerController { }), if (kDebugMode) videoPlayerController!.stream.log.listen(((PlayerLog log) { - debugPrint(log.toString()); + if (log.level == 'error' || log.level == 'fatal') { + Utils.reportError(log.text, null, log.prefix); + } else { + debugPrint(log.toString()); + } })), videoPlayerController!.stream.error.listen((String event) { - if (kDebugMode) { - debugPrint('MPV Exception: $event'); - } if (isFileSource && event.startsWith("Failed to open file")) { return; } diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index 07c62da1b..e8a2ffb2c 100644 --- a/lib/plugin/pl_player/utils/fullscreen.dart +++ b/lib/plugin/pl_player/utils/fullscreen.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:auto_orientation/auto_orientation.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/services.dart'; bool _isDesktopFullScreen = false; diff --git a/lib/tcp/live.dart b/lib/tcp/live.dart index 22ee12f51..fcee13cb1 100644 --- a/lib/tcp/live.dart +++ b/lib/tcp/live.dart @@ -200,36 +200,7 @@ class LiveMessageStream { // ..d('$logTag ===> TCP连接建立') // ..d('$logTag ===> 发送认证包'); _socketSubscription = _channel?.stream.listen( - (data) { - final header = PackageHeaderRes.fromBytesData(data); - if (header != null) { - List decompressedData = []; - //心跳包回复不用处理 - if (header.operationCode == 3) return; - if (header.operationCode == 8) { - _heartBeat(); - } - try { - switch (header.protocolVer) { - case 0: - case 1: - _processingData(data); - return; - case 2: - decompressedData = ZLibDecoder().convert(data.sublist(0x10)); - break; - case 3: - decompressedData = const BrotliDecoder().convert( - data.sublist(0x10), - ); - //debugPrint('Body: ${utf8.decode()}'); - } - _processingData(decompressedData); - } catch (e) { - if (kDebugMode) rethrow; - } - } - }, + onData, onDone: close, onError: (_) => close(), ); @@ -255,7 +226,7 @@ class LiveMessageStream { _processingData(data.sublist(subHeader.totalSize)); } } - } catch (e) { + } catch (_) { if (kDebugMode) rethrow; } } @@ -296,6 +267,37 @@ class LiveMessageStream { _eventListeners.add(func); } + void onData(dynamic data) { + final header = PackageHeaderRes.fromBytesData(data as Uint8List); + if (header != null) { + List decompressedData = []; + //心跳包回复不用处理 + if (header.operationCode == 3) return; + if (header.operationCode == 8) { + _heartBeat(); + } + try { + switch (header.protocolVer) { + case 0: + case 1: + _processingData(data); + return; + case 2: + decompressedData = ZLibDecoder().convert(data.sublist(0x10)); + break; + case 3: + decompressedData = const BrotliDecoder().convert( + data.sublist(0x10), + ); + //debugPrint('Body: ${utf8.decode()}'); + } + _processingData(decompressedData); + } catch (_) { + if (kDebugMode) rethrow; + } + } + } + void close() { _active = false; if (kDebugMode) logger.i("$logTag close $hashCode"); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 1b030c697..0aabb7278 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,19 +1,20 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:math'; +import 'dart:math' show Random; import 'package:PiliPlus/common/constants.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:share_plus/share_plus.dart'; abstract class Utils { - static final Random random = Random(); + static final random = Random(); static const channel = MethodChannel(Constants.appName); @@ -157,4 +158,24 @@ abstract class Utils { final i1 = fileExt ? uri.length : uri.lastIndexOf('.'); return uri.substring(i0, i1); } + + /// When calling this from a `catch` block consider annotating the method + /// containing the `catch` block with + /// `@pragma('vm:notify-debugger-on-exception')` to allow an attached debugger + /// to treat the exception as unhandled. + static void reportError( + Object exception, [ + StackTrace? stack, + String? library = 'PiliPlus', + bool silent = false, + ]) { + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: library, + silent: silent, + ), + ); + } }