mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
fix #1712
fix #1641 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -21,7 +21,6 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_layout.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/marquee.dart' show ContextSingleTicker;
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/image_preview_type.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
@@ -65,7 +64,6 @@ class CustomGridView extends StatelessWidget {
|
||||
required this.maxWidth,
|
||||
required this.picArr,
|
||||
this.onViewImage,
|
||||
this.onDismissed,
|
||||
this.fullScreen = false,
|
||||
});
|
||||
|
||||
@@ -73,7 +71,6 @@ class CustomGridView extends StatelessWidget {
|
||||
final double space;
|
||||
final List<ImageModel> picArr;
|
||||
final VoidCallback? onViewImage;
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final bool fullScreen;
|
||||
|
||||
static bool horizontalPreview = Pref.horizontalPreview;
|
||||
@@ -96,20 +93,18 @@ class CustomGridView extends StatelessWidget {
|
||||
!context.mediaQuerySize.isPortrait) {
|
||||
final scaffoldState = Scaffold.maybeOf(context);
|
||||
if (scaffoldState != null) {
|
||||
onViewImage?.call();
|
||||
PageUtils.onHorizontalPreviewState(
|
||||
scaffoldState,
|
||||
ContextSingleTicker(scaffoldState.context),
|
||||
imgList,
|
||||
index,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
onViewImage?.call();
|
||||
PageUtils.imageView(
|
||||
initialPage: index,
|
||||
imgList: imgList,
|
||||
onDismissed: onDismissed,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import 'package:flutter/material.dart';
|
||||
/// show a [Hero] animation.
|
||||
class HeroDialogRoute<T> extends PageRoute<T> {
|
||||
HeroDialogRoute({
|
||||
required this.builder,
|
||||
required this.pageBuilder,
|
||||
});
|
||||
|
||||
final WidgetBuilder builder;
|
||||
final RoutePageBuilder pageBuilder;
|
||||
|
||||
@override
|
||||
bool get opaque => false;
|
||||
@@ -50,12 +50,10 @@ class HeroDialogRoute<T> extends PageRoute<T> {
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
final Widget child = builder(context);
|
||||
final Widget result = Semantics(
|
||||
return Semantics(
|
||||
scopesRoute: true,
|
||||
explicitChildNodes: true,
|
||||
child: child,
|
||||
child: pageBuilder(context, animation, secondaryAnimation),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,16 +44,12 @@ class InteractiveviewerGallery extends StatefulWidget {
|
||||
this.itemBuilder,
|
||||
this.maxScale = 8,
|
||||
this.minScale = 1.0,
|
||||
this.onPageChanged,
|
||||
this.onDismissed,
|
||||
this.onClose,
|
||||
required this.quality,
|
||||
this.onClose,
|
||||
});
|
||||
|
||||
final int quality;
|
||||
|
||||
final ValueChanged<bool>? onClose;
|
||||
|
||||
/// The sources to show.
|
||||
final List<SourceModel> sources;
|
||||
|
||||
@@ -67,9 +63,7 @@ class InteractiveviewerGallery extends StatefulWidget {
|
||||
|
||||
final double minScale;
|
||||
|
||||
final ValueChanged<int>? onPageChanged;
|
||||
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final VoidCallback? onClose;
|
||||
|
||||
@override
|
||||
State<InteractiveviewerGallery> createState() =>
|
||||
@@ -121,7 +115,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.onClose?.call(true);
|
||||
widget.onClose?.call();
|
||||
_player?.dispose();
|
||||
_pageController.dispose();
|
||||
_animationController
|
||||
@@ -206,7 +200,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
if (item.sourceType == SourceType.livePhoto) {
|
||||
_onPlay(item.liveUrl!);
|
||||
}
|
||||
widget.onPageChanged?.call(page);
|
||||
if (_transformationController.value != Matrix4.identity()) {
|
||||
// animate the reset for the transformation of the interactive viewer
|
||||
|
||||
@@ -228,15 +221,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
: url.http2https;
|
||||
}
|
||||
|
||||
void onClose() {
|
||||
if (widget.onClose != null) {
|
||||
widget.onClose!(false);
|
||||
} else {
|
||||
Get.back();
|
||||
widget.onDismissed?.call(_pageController.page!.floor());
|
||||
}
|
||||
}
|
||||
|
||||
Player? _player;
|
||||
VideoController? _videoController;
|
||||
|
||||
@@ -254,7 +238,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
onNoBoundaryHit: _onNoBoundaryHit,
|
||||
maxScale: widget.maxScale,
|
||||
minScale: widget.minScale,
|
||||
onDismissed: onClose,
|
||||
onDismissed: Get.back,
|
||||
onReset: () {
|
||||
if (!_enablePageView) {
|
||||
setState(() {
|
||||
@@ -277,7 +261,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
onTap: () => EasyThrottle.throttle(
|
||||
'preview',
|
||||
const Duration(milliseconds: 555),
|
||||
onClose,
|
||||
Get.back,
|
||||
),
|
||||
onDoubleTapDown: (TapDownDetails details) {
|
||||
_doubleTapLocalPosition = details.localPosition;
|
||||
|
||||
@@ -98,7 +98,7 @@ class VideoDetailController extends GetxController
|
||||
final Rx<LoadingState> videoState = LoadingState.loading().obs;
|
||||
|
||||
/// 播放器配置 画质 音质 解码格式
|
||||
late Rx<VideoQuality> currentVideoQa;
|
||||
final Rxn<VideoQuality> currentVideoQa = Rxn<VideoQuality>();
|
||||
AudioQuality? currentAudioQa;
|
||||
late VideoDecodeFormatType currentDecodeFormats;
|
||||
|
||||
@@ -239,15 +239,7 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
bool imageStatus = false;
|
||||
|
||||
void onViewImage() {
|
||||
imageStatus = true;
|
||||
}
|
||||
|
||||
void onDismissed(int _) {
|
||||
imageStatus = false;
|
||||
}
|
||||
bool imageview = false;
|
||||
|
||||
final isLoginVideo = Accounts.get(AccountType.video).isLogin;
|
||||
|
||||
@@ -993,7 +985,7 @@ class VideoDetailController extends GetxController
|
||||
SmartDialog.showToast('UP主已关闭弹幕');
|
||||
return;
|
||||
}
|
||||
final isPlaying = plPlayerController.playerStatus.playing;
|
||||
final isPlaying = autoPlay.value && plPlayerController.playerStatus.playing;
|
||||
if (isPlaying) {
|
||||
await plPlayerController.pause();
|
||||
}
|
||||
@@ -1063,6 +1055,8 @@ class VideoDetailController extends GetxController
|
||||
|
||||
/// 更新画质、音质
|
||||
void updatePlayer() {
|
||||
final currentVideoQa = this.currentVideoQa.value;
|
||||
if (currentVideoQa == null) return;
|
||||
autoPlay.value = true;
|
||||
playedTime = plPlayerController.position.value;
|
||||
plPlayerController
|
||||
@@ -1070,7 +1064,7 @@ class VideoDetailController extends GetxController
|
||||
..isBuffering.value = false
|
||||
..buffered.value = Duration.zero;
|
||||
|
||||
final video = findVideoByQa(currentVideoQa.value.code);
|
||||
final video = findVideoByQa(currentVideoQa.code);
|
||||
if (firstVideo.codecs != video.codecs) {
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString(video.codecs!);
|
||||
}
|
||||
@@ -1089,6 +1083,15 @@ class VideoDetailController extends GetxController
|
||||
playerInit();
|
||||
}
|
||||
|
||||
FutureOr<void> _initPlayerIfNeeded() {
|
||||
if ((autoPlay.value ||
|
||||
(plPlayerController.preInitPlayer &&
|
||||
!plPlayerController.processing)) &&
|
||||
childKey.currentState?.mounted == true) {
|
||||
return playerInit();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> playerInit({
|
||||
String? video,
|
||||
String? audio,
|
||||
@@ -1248,10 +1251,8 @@ class VideoDetailController extends GetxController
|
||||
);
|
||||
setVideoHeight();
|
||||
currentDecodeFormats = VideoDecodeFormatType.fromString('avc1');
|
||||
currentVideoQa = Rx(VideoQuality.fromCode(data.quality!));
|
||||
if (autoPlay.value || plPlayerController.preInitPlayer) {
|
||||
await playerInit();
|
||||
}
|
||||
currentVideoQa.value = VideoQuality.fromCode(data.quality!);
|
||||
await _initPlayerIfNeeded();
|
||||
isQuerying = false;
|
||||
return;
|
||||
}
|
||||
@@ -1282,7 +1283,7 @@ class VideoDetailController extends GetxController
|
||||
numbers,
|
||||
);
|
||||
}
|
||||
currentVideoQa = Rx(VideoQuality.fromCode(resVideoQa));
|
||||
currentVideoQa.value = VideoQuality.fromCode(resVideoQa);
|
||||
|
||||
/// 取出符合当前画质的videoList
|
||||
final List<VideoItem> videosList = allVideosList
|
||||
@@ -1354,9 +1355,7 @@ class VideoDetailController extends GetxController
|
||||
firstAudio = AudioItem();
|
||||
audioUrl = '';
|
||||
}
|
||||
if (autoPlay.value || plPlayerController.preInitPlayer) {
|
||||
await playerInit();
|
||||
}
|
||||
await _initPlayerIfNeeded();
|
||||
} else {
|
||||
autoPlay.value = false;
|
||||
videoState.value = result..toast();
|
||||
|
||||
@@ -145,10 +145,8 @@ class _PgcIntroPageState extends State<PgcIntroPage> {
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
videoDetailCtr.onViewImage();
|
||||
PageUtils.imageView(
|
||||
imgList: [SourceModel(url: item.cover!)],
|
||||
onDismissed: videoDetailCtr.onDismissed,
|
||||
);
|
||||
},
|
||||
child: Hero(
|
||||
|
||||
@@ -398,10 +398,8 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
|
||||
|
||||
Widget _buildAvatar(String face) => GestureDetector(
|
||||
onTap: () {
|
||||
widget.videoDetailController.onViewImage();
|
||||
PageUtils.imageView(
|
||||
imgList: [SourceModel(url: face)],
|
||||
onDismissed: widget.videoDetailController.onDismissed,
|
||||
);
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
|
||||
@@ -20,15 +20,11 @@ class VideoReplyPanel extends StatefulWidget {
|
||||
super.key,
|
||||
this.replyLevel = 1,
|
||||
required this.heroTag,
|
||||
this.onViewImage,
|
||||
this.onDismissed,
|
||||
required this.isNested,
|
||||
});
|
||||
|
||||
final int replyLevel;
|
||||
final String heroTag;
|
||||
final VoidCallback? onViewImage;
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final bool isNested;
|
||||
|
||||
@override
|
||||
@@ -216,8 +212,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
_videoReplyController.onRemove(index, item, subIndex),
|
||||
upMid: _videoReplyController.upMid,
|
||||
getTag: () => heroTag,
|
||||
onViewImage: widget.onViewImage,
|
||||
onDismissed: widget.onDismissed,
|
||||
onCheckReply: (item) => _videoReplyController
|
||||
.onCheckReply(item, isManual: true),
|
||||
onToggleTop: (item) => _videoReplyController.onToggleTop(
|
||||
@@ -258,8 +252,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
firstFloor: replyItem,
|
||||
replyType: _videoReplyController.videoType.replyType,
|
||||
isVideoDetail: true,
|
||||
onViewImage: widget.onViewImage,
|
||||
onDismissed: widget.onDismissed,
|
||||
isNested: widget.isNested,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -50,7 +50,6 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
this.showDialogue,
|
||||
this.getTag,
|
||||
this.onViewImage,
|
||||
this.onDismissed,
|
||||
this.onCheckReply,
|
||||
this.onToggleTop,
|
||||
this.jumpToDialogue,
|
||||
@@ -65,7 +64,6 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
final VoidCallback? showDialogue;
|
||||
final Function? getTag;
|
||||
final VoidCallback? onViewImage;
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final ValueChanged<ReplyInfo>? onCheckReply;
|
||||
final ValueChanged<ReplyInfo>? onToggleTop;
|
||||
final VoidCallback? jumpToDialogue;
|
||||
@@ -317,7 +315,6 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
)
|
||||
.toList(),
|
||||
onViewImage: onViewImage,
|
||||
onDismissed: onDismissed,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -29,8 +29,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
|
||||
this.firstFloor,
|
||||
required this.isVideoDetail,
|
||||
required this.replyType,
|
||||
this.onViewImage,
|
||||
this.onDismissed,
|
||||
this.isNested = false,
|
||||
});
|
||||
final int? id;
|
||||
@@ -40,8 +38,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
|
||||
final ReplyInfo? firstFloor;
|
||||
final bool isVideoDetail;
|
||||
final int replyType;
|
||||
final VoidCallback? onViewImage;
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final bool isNested;
|
||||
|
||||
@override
|
||||
@@ -231,8 +227,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
||||
index: -1,
|
||||
),
|
||||
upMid: _controller.upMid,
|
||||
onViewImage: widget.onViewImage,
|
||||
onDismissed: widget.onDismissed,
|
||||
onCheckReply: (item) =>
|
||||
_controller.onCheckReply(item, isManual: true),
|
||||
),
|
||||
@@ -390,8 +384,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
||||
SmartDialog.showToast('评论可能已被删除');
|
||||
}
|
||||
},
|
||||
onViewImage: widget.onViewImage,
|
||||
onDismissed: widget.onDismissed,
|
||||
onCheckReply: (item) => _controller.onCheckReply(item, isManual: true),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'dart:ui';
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/hero_dialog_route.dart';
|
||||
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
@@ -364,7 +365,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@override
|
||||
// 离开当前页面时
|
||||
Future<void> didPushNext() async {
|
||||
if (videoDetailController.imageStatus) {
|
||||
if (Get.routing.route is HeroDialogRoute) {
|
||||
videoDetailController.imageview = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -395,7 +397,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@override
|
||||
// 返回当前页面时
|
||||
Future<void> didPopNext() async {
|
||||
if (videoDetailController.imageStatus) {
|
||||
if (videoDetailController.imageview) {
|
||||
videoDetailController.imageview = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -519,7 +522,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
..addListener(animListener);
|
||||
if (Utils.isMobile && mounted && isShowing && !isFullScreen) {
|
||||
if (isPortrait) {
|
||||
if (!videoDetailController.imageStatus) {
|
||||
if (!videoDetailController.imageview) {
|
||||
showStatusBar();
|
||||
}
|
||||
} else if (!videoDetailController.horizontalScreen) {
|
||||
@@ -1944,8 +1947,6 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
key: videoReplyPanelKey,
|
||||
isNested: isNested,
|
||||
heroTag: heroTag,
|
||||
onViewImage: videoDetailController.onViewImage,
|
||||
onDismissed: videoDetailController.onDismissed,
|
||||
);
|
||||
|
||||
// ai总结
|
||||
|
||||
@@ -493,7 +493,7 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
leading: const Icon(Icons.play_circle_outline, size: 20),
|
||||
title: const Text('选择画质', style: titleStyle),
|
||||
subtitle: Text(
|
||||
'当前画质 ${videoDetailCtr.currentVideoQa.value.desc}',
|
||||
'当前画质 ${videoDetailCtr.currentVideoQa.value?.desc}',
|
||||
style: subTitleStyle,
|
||||
),
|
||||
),
|
||||
@@ -759,7 +759,8 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
return;
|
||||
}
|
||||
final List<FormatItem> videoFormat = videoInfo.supportFormats!;
|
||||
final VideoQuality currentVideoQa = videoDetailCtr.currentVideoQa.value;
|
||||
final VideoQuality? currentVideoQa = videoDetailCtr.currentVideoQa.value;
|
||||
if (currentVideoQa == null) return;
|
||||
|
||||
/// 总质量分类
|
||||
final int totalQaSam = videoFormat.length;
|
||||
|
||||
@@ -621,6 +621,9 @@ class PlPlayerController {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
bool _processing = false;
|
||||
bool get processing => _processing;
|
||||
|
||||
// 初始化资源
|
||||
Future<void> setDataSource(
|
||||
DataSource dataSource, {
|
||||
@@ -649,6 +652,7 @@ class PlPlayerController {
|
||||
Volume? volume,
|
||||
}) async {
|
||||
try {
|
||||
_processing = true;
|
||||
this.isLive = isLive;
|
||||
_videoType = videoType ?? VideoType.ugc;
|
||||
this.width = width;
|
||||
@@ -709,6 +713,8 @@ class PlPlayerController {
|
||||
debugPrint(stackTrace.toString());
|
||||
debugPrint('plPlayer err: $err');
|
||||
}
|
||||
} finally {
|
||||
_processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -717,8 +717,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
BottomControlType.qa => Obx(
|
||||
() {
|
||||
final VideoQuality currentVideoQa =
|
||||
final VideoQuality? currentVideoQa =
|
||||
videoDetailController.currentVideoQa.value;
|
||||
if (currentVideoQa == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final PlayUrlModel videoInfo = videoDetailController.data;
|
||||
if (videoInfo.dash == null) {
|
||||
return const SizedBox.shrink();
|
||||
|
||||
@@ -41,17 +41,16 @@ abstract class PageUtils {
|
||||
static Future<void> imageView({
|
||||
int initialPage = 0,
|
||||
required List<SourceModel> imgList,
|
||||
ValueChanged<int>? onDismissed,
|
||||
int? quality,
|
||||
}) {
|
||||
return Get.key.currentState!.push<void>(
|
||||
HeroDialogRoute(
|
||||
builder: (context) => InteractiveviewerGallery(
|
||||
sources: imgList,
|
||||
initIndex: initialPage,
|
||||
onDismissed: onDismissed,
|
||||
quality: quality ?? GlobalData().imgQuality,
|
||||
),
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
InteractiveviewerGallery(
|
||||
sources: imgList,
|
||||
initIndex: initialPage,
|
||||
quality: quality ?? GlobalData().imgQuality,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -555,42 +554,28 @@ abstract class PageUtils {
|
||||
|
||||
static void onHorizontalPreviewState(
|
||||
ScaffoldState state,
|
||||
TickerProvider vsync,
|
||||
List<SourceModel> imgList,
|
||||
int index,
|
||||
) {
|
||||
final ctr = AnimationController(
|
||||
vsync: vsync,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
final animController = AnimationController(
|
||||
vsync: state,
|
||||
duration: Duration.zero,
|
||||
reverseDuration: Duration.zero,
|
||||
)..forward();
|
||||
state.showBottomSheet(
|
||||
constraints: const BoxConstraints(),
|
||||
(context) {
|
||||
return FadeTransition(
|
||||
opacity: Tween<double>(begin: 0, end: 1).animate(ctr),
|
||||
child: InteractiveviewerGallery(
|
||||
sources: imgList,
|
||||
initIndex: index,
|
||||
onClose: (value) async {
|
||||
if (!value) {
|
||||
try {
|
||||
await ctr.reverse();
|
||||
} catch (_) {}
|
||||
}
|
||||
try {
|
||||
ctr.dispose();
|
||||
} catch (_) {}
|
||||
if (!value) {
|
||||
Get.back();
|
||||
}
|
||||
},
|
||||
quality: GlobalData().imgQuality,
|
||||
),
|
||||
return InteractiveviewerGallery(
|
||||
sources: imgList,
|
||||
initIndex: index,
|
||||
quality: GlobalData().imgQuality,
|
||||
onClose: animController.dispose,
|
||||
);
|
||||
},
|
||||
enableDrag: false,
|
||||
elevation: 0.0,
|
||||
backgroundColor: Colors.transparent,
|
||||
transitionAnimationController: animController,
|
||||
sheetAnimationStyle: const AnimationStyle(duration: Duration.zero),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user