merge main

This commit is contained in:
guozhigq
2023-11-12 12:09:16 +08:00
185 changed files with 10201 additions and 2649 deletions

View File

@ -1,25 +1,26 @@
import 'dart:async';
import 'dart:io';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:floating/floating.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/common/widgets/sliver_header.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/pages/bangumi/introduction/index.dart';
import 'package:pilipala/pages/danmaku/view.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
import 'package:pilipala/pages/video/detail/reply/index.dart';
import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/introduction/index.dart';
import 'package:pilipala/pages/video/detail/related/index.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/storage.dart';
import 'widgets/app_bar.dart';
import 'widgets/header_control.dart';
class VideoDetailPage extends StatefulWidget {
@ -32,14 +33,14 @@ class VideoDetailPage extends StatefulWidget {
}
class _VideoDetailPageState extends State<VideoDetailPage>
with TickerProviderStateMixin, RouteAware {
final VideoDetailController videoDetailController =
Get.put(VideoDetailController(), tag: Get.arguments['heroTag']);
with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver {
late VideoDetailController videoDetailController;
PlPlayerController? plPlayerController;
final ScrollController _extendNestCtr = ScrollController();
late StreamController<double> appbarStream;
final VideoIntroController videoIntroController =
Get.put(VideoIntroController(), tag: Get.arguments['heroTag']);
late VideoIntroController videoIntroController;
late BangumiIntroController bangumiIntroController;
late String heroTag;
PlayerStatus playerStatus = PlayerStatus.playing;
double doubleOffset = 0;
@ -51,15 +52,39 @@ class _VideoDetailPageState extends State<VideoDetailPage>
late Future _futureBuilderFuture;
// 自动退出全屏
late bool autoExitFullcreen;
late bool autoPlayEnable;
late bool autoPiP;
final floating = Floating();
@override
void initState() {
super.initState();
heroTag = Get.arguments['heroTag'];
videoDetailController = Get.put(VideoDetailController(), tag: heroTag);
videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
videoIntroController.videoDetail.listen((value) {
videoPlayerServiceHandler.onVideoDetailChange(
value, videoDetailController.cid.value);
});
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
bangumiIntroController.bangumiDetail.listen((value) {
videoPlayerServiceHandler.onVideoDetailChange(
value, videoDetailController.cid.value);
});
videoDetailController.cid.listen((p0) {
videoPlayerServiceHandler.onVideoDetailChange(
bangumiIntroController.bangumiDetail.value, p0);
});
statusBarHeight = localCache.get('statusBarHeight');
autoExitFullcreen =
setting.get(SettingBoxKey.enableAutoExit, defaultValue: false);
autoPlayEnable =
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true);
autoPiP = setting.get(SettingBoxKey.autoPiP, defaultValue: false);
videoSourceInit();
appbarStreamListen();
WidgetsBinding.instance.addObserver(this);
}
// 获取视频资源,初始化播放器
@ -83,15 +108,38 @@ class _VideoDetailPageState extends State<VideoDetailPage>
}
// 播放器状态监听
void playerListener(PlayerStatus? status) {
void playerListener(PlayerStatus? status) async {
playerStatus = status!;
if (status == PlayerStatus.completed) {
// 结束播放退出全屏
if (autoExitFullcreen) {
plPlayerController!.triggerFullScreen(status: false);
}
/// 顺序播放 列表循环
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
if (videoDetailController.videoType == SearchType.video) {
videoIntroController.nextPlay();
}
if (videoDetailController.videoType == SearchType.media_bangumi) {
bangumiIntroController.nextPlay();
}
}
/// 单个循环
if (plPlayerController!.playRepeat == PlayRepeat.singleCycle) {
plPlayerController!.seekTo(Duration.zero);
plPlayerController!.play();
}
// 播放完展示控制栏
plPlayerController!.onLockControl(false);
try {
PiPStatus currentStatus =
await videoDetailController.floating!.pipStatus;
if (currentStatus == PiPStatus.disabled) {
plPlayerController!.onLockControl(false);
}
} catch (_) {}
}
}
@ -102,6 +150,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController!.play();
}
/// 未开启自动播放时触发播放
Future<void> handlePlay() async {
await videoDetailController.playerInit();
plPlayerController = videoDetailController.plPlayerController;
@ -111,8 +160,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override
void dispose() {
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.dispose();
if (plPlayerController != null) {
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.dispose();
}
if (videoDetailController.floating != null) {
videoDetailController.floating!.dispose();
}
videoPlayerServiceHandler.onVideoDetailDispose();
WidgetsBinding.instance.removeObserver(this);
floating.dispose();
super.dispose();
}
@ -123,10 +180,12 @@ class _VideoDetailPageState extends State<VideoDetailPage>
if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false)) {
videoDetailController.brightness = plPlayerController!.brightness.value;
}
videoDetailController.defaultST = plPlayerController!.position.value;
videoIntroController.isPaused = true;
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.pause();
if (plPlayerController != null) {
videoDetailController.defaultST = plPlayerController!.position.value;
videoIntroController.isPaused = true;
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.pause();
}
super.didPushNext();
}
@ -134,13 +193,19 @@ class _VideoDetailPageState extends State<VideoDetailPage>
// 返回当前页面时
void didPopNext() async {
videoDetailController.isFirstTime = false;
videoDetailController.playerInit();
bool autoplay = autoPlayEnable;
videoDetailController.playerInit(autoplay: autoplay);
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
videoDetailController.autoPlay.value =
!videoDetailController.isShowCover.value;
videoIntroController.isPaused = false;
if (_extendNestCtr.position.pixels == 0) {
if (_extendNestCtr.position.pixels == 0 && autoplay) {
await Future.delayed(const Duration(milliseconds: 300));
plPlayerController!.play();
plPlayerController!.seekTo(videoDetailController.defaultST);
plPlayerController?.play();
}
plPlayerController!.addStatusLister(playerListener);
plPlayerController?.addStatusLister(playerListener);
super.didPopNext();
}
@ -151,12 +216,23 @@ class _VideoDetailPageState extends State<VideoDetailPage>
.subscribe(this, ModalRoute.of(context) as PageRoute);
}
@override
void didChangeAppLifecycleState(AppLifecycleState lifecycleState) {
if (lifecycleState == AppLifecycleState.inactive && autoPiP) {
floating.enable(
aspectRatio: Rational(
videoDetailController.data.dash!.video!.first.width!,
videoDetailController.data.dash!.video!.first.height!,
));
}
}
@override
Widget build(BuildContext context) {
final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
final double pinnedHeaderHeight =
statusBarHeight + kToolbarHeight + videoHeight;
return SafeArea(
Widget childWhenDisabled = SafeArea(
top: false,
bottom: false,
child: Stack(
@ -198,12 +274,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
? const SizedBox()
: PLVideoPlayer(
controller: plPlayerController!,
headerControl: HeaderControl(
controller:
plPlayerController,
videoDetailCtr:
videoDetailController,
),
headerControl:
videoDetailController
.headerControl,
danmuWidget: Obx(
() => PlDanmaku(
key: Key(
@ -336,13 +409,18 @@ class _VideoDetailPageState extends State<VideoDetailPage>
),
];
},
// pinnedHeaderSliverHeightBuilder: () {
// return playerStatus != PlayerStatus.playing
// ? statusBarHeight + kToolbarHeight
// : pinnedHeaderHeight;
// },
/// 不收回
pinnedHeaderSliverHeightBuilder: () {
return playerStatus != PlayerStatus.playing
? statusBarHeight + kToolbarHeight
: pinnedHeaderHeight;
return pinnedHeaderHeight;
},
onlyOneScrollInBody: true,
body: Container(
key: Key(heroTag),
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
@ -378,8 +456,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
const VideoIntroPanel(),
] else if (videoDetailController.videoType ==
SearchType.media_bangumi) ...[
BangumiIntroPanel(
cid: videoDetailController.cid)
Obx(() => BangumiIntroPanel(
cid: videoDetailController.cid.value)),
],
// if (videoDetailController.videoType ==
// SearchType.video) ...[
@ -418,21 +496,61 @@ class _VideoDetailPageState extends State<VideoDetailPage>
),
),
),
/// 重新进入会刷新
// 播放完成/暂停播放
StreamBuilder(
stream: appbarStream.stream,
initialData: 0,
builder: ((context, snapshot) {
return ScrollAppBar(
snapshot.data!.toDouble(),
() => continuePlay(),
playerStatus,
null,
);
}),
)
// StreamBuilder(
// stream: appbarStream.stream,
// initialData: 0,
// builder: ((context, snapshot) {
// return ScrollAppBar(
// snapshot.data!.toDouble(),
// () => continuePlay(),
// playerStatus,
// null,
// );
// }),
// )
],
),
);
Widget childWhenEnabled = FutureBuilder(
key: Key(heroTag),
future: _futureBuilderFuture,
builder: ((context, snapshot) {
if (snapshot.hasData && snapshot.data['status']) {
return Obx(
() => !videoDetailController.autoPlay.value
? const SizedBox()
: PLVideoPlayer(
controller: plPlayerController!,
headerControl: HeaderControl(
controller: plPlayerController,
videoDetailCtr: videoDetailController,
),
danmuWidget: Obx(
() => PlDanmaku(
key: Key(
videoDetailController.danmakuCid.value.toString()),
cid: videoDetailController.danmakuCid.value,
playerController: plPlayerController!,
),
),
),
);
} else {
return const SizedBox();
}
}),
);
if (Platform.isAndroid) {
return PiPSwitcher(
childWhenDisabled: childWhenDisabled,
childWhenEnabled: childWhenEnabled,
floating: floating,
);
} else {
return childWhenDisabled;
}
}
}