@@ -133,116 +133,107 @@ class _PlayerItemState extends State<PlayerItem>
133133 }
134134
135135 void _loadShortcuts () {
136- keyboardShortcuts = {
137- 'playorpause' : setting.get ('shortcut_playorpause' , defaultValue: [' ' ]).cast <String >(),
138- 'forward' : setting.get ('shortcut_forward' , defaultValue: ['Arrow Right' ]).cast <String >(),
139- 'rewind' : setting.get ('shortcut_rewind' , defaultValue: ['Arrow Left' ]).cast <String >(),
140- 'volumeup' : setting.get ('shortcut_volumeup' , defaultValue: ['Arrow Up' ]).cast <String >(),
141- 'volumedown' : setting.get ('shortcut_volumedown' , defaultValue: ['Arrow Down' ]).cast <String >(),
142- 'togglemute' : setting.get ('shortcut_togglemute' , defaultValue: ['M' ]).cast <String >(),
143- 'fullscreen' : setting.get ('shortcut_fullscreen' , defaultValue: ['F' ]).cast <String >(),
144- 'exitfullscreen' : setting.get ('shortcut_exitfullscreen' , defaultValue: ['Escape' ]).cast <String >(),
145- 'screenshot' : setting.get ('shortcut_screenshot' , defaultValue: ['S' ]).cast <String >(),
146- 'skip' : setting.get ('shortcut_skip' , defaultValue: ['K' ]).cast <String >(),
147- 'toggledanmaku' : setting.get ('shortcut_toggledanmaku' , defaultValue: ['D' ]).cast <String >(),
148- 'speed1' : setting.get ('shortcut_speed1' , defaultValue: ['1' ]).cast <String >(),
149- 'speed2' : setting.get ('shortcut_speed2' , defaultValue: ['2' ]).cast <String >(),
150- 'speed3' : setting.get ('shortcut_speed3' , defaultValue: ['3' ]).cast <String >(),
151- };
136+ keyboardShortcuts = {};
137+ defaultShortcuts.forEach ((key, defaultValue) {
138+ keyboardShortcuts[key] = setting
139+ .get ('shortcut_$key ' , defaultValue: defaultValue)
140+ .cast <String >();
141+ });
152142 }
153143
154144 void _initKeyboardActions (){
155145 keyboardActions = {
156- 'playorpause' : () async {
157- try {
158- playerController.playOrPause ();
159- } catch (e) {
160- KazumiLogger ().log (
161- Level .error, '播放器内部错误 ${e .toString ()}' );
162- }
163- },
164- 'forward' : () async {
165- int targetPosition = playerController.currentPosition.inSeconds +
166- playerController.arrowKeySkipTime;
167-
168- final total = playerController.playerDuration.inSeconds;
169- if (targetPosition > total) {
170- targetPosition = total;
171- }
172-
173- try {
174- playerTimer? .cancel ();
175- playerController.seek (
176- Duration (seconds: targetPosition),
177- );
178- playerTimer = getPlayerTimer ();
179- } catch (e) {
180- KazumiLogger ()
181- .log (Level .error, e.toString ()
182- );
183- }
184- },
185- 'rewind' : () async {
186- int targetPosition = playerController
187- .currentPosition.inSeconds -
188- playerController.arrowKeySkipTime;
189- if (targetPosition < 0 ) {
190- targetPosition = 0 ;
191- }
192- try {
193- playerTimer? .cancel ();
194- playerController.seek (
195- Duration (seconds: targetPosition));
196- playerTimer = getPlayerTimer ();
197- } catch (e) {
198- KazumiLogger ()
199- .log (Level .error, e.toString ());
200- }
201- },
202- 'volumeup' : () async {
203- increaseVolume ();
204- _handleKeyChangingVolume ();
205- },
206- 'volumedown' : () async {
207- decreaseVolume ();
208- _handleKeyChangingVolume ();
209- },
210- 'togglemute' : () async {
211- toggleMute ();
212- _handleKeyChangingVolume ();
213- },
214- 'fullscreen' : () async {
215- if (! videoPageController.isPip) handleFullscreen ();
216- },
217- 'screenshot' : () async {
218- _handleScreenshot ();
219- },
220- 'skip' : () async {
221- playerController.seek (playerController.currentPosition +
222- Duration (seconds: playerController.buttonSkipTime));
223- },
224- 'exitfullscreen' : () async {
225- if (videoPageController.isFullscreen &&
226- ! Utils .isTablet ()) {
227- try {
228- playerController.danmakuController
229- .clear ();
230- } catch (_) {}
231- Utils .exitFullScreen ();
232- videoPageController.isFullscreen =
233- ! videoPageController.isFullscreen;
234- } else if (! Platform .isMacOS) {
235- playerController.pause ();
236- windowManager.hide ();
237- }
238- },
146+ 'playorpause' : () => playerController.playOrPause (),
147+ 'forward' : () async => handleShortcutSeek ('forward' ),
148+ 'rewind' : () async => handleShortcutSeek ('rewind' ),
149+ 'next' : () async => handlePreNextEpisode ('next' ),
150+ 'prev' : () async => handlePreNextEpisode ('prev' ),
151+ 'volumeup' : () async => handleShortcutVolumeChange ('up' ),
152+ 'volumedown' : () async => handleShortcutVolumeChange ('down' ),
153+ 'togglemute' : () async => handleShortcutVolumeChange ('mute' ),
154+ 'fullscreen' : () => handleShortcutFullscreen (),
155+ 'screenshot' : () async => handleScreenshot (),
156+ 'skip' : () async => skipOP (),
157+ 'exitfullscreen' : () => handleShortcutExitFullscreen (),
239158 'toggledanmaku' : handleDanmaku,
240- 'speed1' : () => setPlaybackSpeed (1.0 ),
241- 'speed2' : () => setPlaybackSpeed (2.0 ),
242- 'speed3' : () => setPlaybackSpeed (3.0 ),
159+ 'speed1' : () async => setPlaybackSpeed (1.0 ),
160+ 'speed2' : () async => setPlaybackSpeed (2.0 ),
161+ 'speed3' : () async => setPlaybackSpeed (3.0 ),
243162 };
244163 }
245164
165+ //上一集下一集动作
166+ Future <void > handlePreNextEpisode (String direction) async {
167+ if (videoPageController.loading) return ;
168+ final currentRoad = videoPageController.currentRoad;
169+ final episodes = videoPageController.roadList[currentRoad].data;
170+ int targetEpisode;
171+ if (direction == 'next' ){targetEpisode = videoPageController.currentEpisode + 1 ;}
172+ else if (direction == 'prev' ) {targetEpisode = videoPageController.currentEpisode - 1 ;}
173+ else {return ;}
174+
175+ if (targetEpisode > episodes.length) {
176+ KazumiDialog .showToast (message: '已经是最新一集' );
177+ return ;
178+ }
179+ if (targetEpisode <= 0 ) {
180+ KazumiDialog .showToast (message: '已经是第一集' );
181+ return ;
182+ }
183+
184+ final identifier = videoPageController.roadList[currentRoad].identifier[targetEpisode - 1 ];
185+ KazumiDialog .showToast (message: '正在加载$identifier ' );
186+ widget.changeEpisode (targetEpisode, currentRoad: currentRoad);
187+ }
188+
189+ //快进快退快捷键动作
190+ Future <void > handleShortcutSeek (String direction) async {
191+ int skipTime = playerController.arrowKeySkipTime;
192+ int current = playerController.currentPosition.inSeconds;
193+ int total = playerController.playerDuration.inSeconds;
194+ int targetPosition;
195+
196+ if (direction == 'forward' ) {
197+ targetPosition = current + skipTime;
198+ if (targetPosition > total) targetPosition = total;
199+ } else if (direction == 'rewind' ) {
200+ targetPosition = current - skipTime;
201+ if (targetPosition < 0 ) targetPosition = 0 ;
202+ } else {
203+ return ;
204+ }
205+
206+ try {
207+ playerTimer? .cancel ();
208+ await playerController.seek (Duration (seconds: targetPosition));
209+ playerTimer = getPlayerTimer ();
210+ } catch (e) {
211+ KazumiLogger ().log (Level .error, e.toString ());
212+ }
213+ }
214+
215+ //全屏快捷键动作
216+ void handleShortcutFullscreen (){
217+ if (! videoPageController.isPip) handleFullscreen ();
218+ }
219+
220+ //退出全屏快捷键动作
221+ void handleShortcutExitFullscreen (){
222+ if (videoPageController.isFullscreen &&
223+ ! Utils .isTablet ()) {
224+ try {
225+ playerController.danmakuController
226+ .clear ();
227+ } catch (_) {}
228+ Utils .exitFullScreen ();
229+ videoPageController.isFullscreen =
230+ ! videoPageController.isFullscreen;
231+ } else if (! Platform .isMacOS) {
232+ playerController.pause ();
233+ windowManager.hide ();
234+ }
235+ }
236+
246237 void _handleTap () {
247238 if (Utils .isDesktop ()) {
248239 playerController.playOrPause ();
@@ -281,16 +272,10 @@ class _PlayerItemState extends State<PlayerItem>
281272 mouseScrollerTimer = null ;
282273 });
283274 }
284-
285- void _handleKeyChangingVolume () {
286- playerController.showVolume = true ;
287- hideVolumeUITimer? .cancel ();
288- hideVolumeUITimer = Timer (const Duration (seconds: 2 ), () {
289- if (mounted) {
290- playerController.showVolume = false ;
291- }
292- hideVolumeUITimer = null ;
293- });
275+
276+ //跳过指定秒数
277+ Future <void > skipOP () async {
278+ await playerController.seek (playerController.currentPosition + Duration (seconds: playerController.buttonSkipTime));
294279 }
295280
296281 void handleDanmaku () {
@@ -344,8 +329,9 @@ class _PlayerItemState extends State<PlayerItem>
344329 playerTimer? .cancel ();
345330 playerTimer = getPlayerTimer ();
346331 }
347-
348- void _handleScreenshot () async {
332+
333+ //截图
334+ Future <void > handleScreenshot () async {
349335 KazumiDialog .showToast (message: '截图中...' );
350336 try {
351337 Uint8List ? screenshot =
@@ -476,20 +462,36 @@ class _PlayerItemState extends State<PlayerItem>
476462 await playerController.setPlaybackSpeed (speed);
477463 }
478464
479- Future <void > increaseVolume () async {
480- await playerController.setVolume (playerController.volume + 10 );
481- }
482-
483- Future <void > decreaseVolume () async {
484- await playerController.setVolume (playerController.volume - 10 );
485- }
486-
487- Future <void > toggleMute () async {
488- if (playerController.volume > 0 ) {
489- lastVolume = playerController.volume;
490- await playerController.setVolume (0 );
491- } else {
492- await playerController.setVolume (lastVolume);
465+ Future <void > handleShortcutVolumeChange (String type) async {
466+ try {
467+ switch (type) {
468+ case 'up' :
469+ await playerController.setVolume (playerController.volume + 10 );
470+ break ;
471+ case 'down' :
472+ await playerController.setVolume (playerController.volume - 10 );
473+ break ;
474+ case 'mute' :
475+ if (playerController.volume > 0 ) {
476+ lastVolume = playerController.volume;
477+ await playerController.setVolume (0 );
478+ } else {
479+ await playerController.setVolume (lastVolume);
480+ }
481+ break ;
482+ default :
483+ return ;
484+ }
485+ playerController.showVolume = true ;
486+ hideVolumeUITimer? .cancel ();
487+ hideVolumeUITimer = Timer (const Duration (seconds: 2 ), () {
488+ if (mounted) {
489+ playerController.showVolume = false ;
490+ }
491+ hideVolumeUITimer = null ;
492+ });
493+ } catch (e) {
494+ KazumiLogger ().log (Level .error, '音量操作失败: ${e .toString ()}' );
493495 }
494496 }
495497
@@ -1386,6 +1388,7 @@ class _PlayerItemState extends State<PlayerItem>
13861388 handleProgressBarDragEnd: handleProgressBarDragEnd,
13871389 handleSuperResolutionChange:
13881390 handleSuperResolutionChange,
1391+ handlePreNextEpisode: handlePreNextEpisode,
13891392 animationController: animationController! ,
13901393 keyboardFocus: widget.keyboardFocus,
13911394 sendDanmaku: widget.sendDanmaku,
@@ -1398,7 +1401,8 @@ class _PlayerItemState extends State<PlayerItem>
13981401 showSyncPlayEndPointSwitchDialog:
13991402 showSyncPlayEndPointSwitchDialog,
14001403 disableAnimations: widget.disableAnimations,
1401- handleScreenShot: _handleScreenshot,
1404+ handleScreenShot: handleScreenshot,
1405+ skipOP: skipOP,
14021406 )
14031407 : SmallestPlayerItemPanel (
14041408 onBackPressed: widget.onBackPressed,
@@ -1422,6 +1426,7 @@ class _PlayerItemState extends State<PlayerItem>
14221426 showSyncPlayEndPointSwitchDialog:
14231427 showSyncPlayEndPointSwitchDialog,
14241428 disableAnimations: widget.disableAnimations,
1429+ skipOP: skipOP,
14251430 ),
14261431 // 播放器手势控制
14271432 Positioned .fill (
0 commit comments