opt intro controller

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-08-02 14:07:29 +08:00
parent 199ddc0e7e
commit 3c964787df
19 changed files with 507 additions and 446 deletions

View File

@@ -1,12 +1,16 @@
import 'dart:async' show Timer;
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/models_new/video/video_detail/data.dart';
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/id_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
@@ -17,6 +21,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
abstract class CommonIntroController extends GetxController {
String heroTag = Get.arguments['heroTag'];
String bvid = Get.parameters['bvid']!;
// 是否点赞
@@ -27,6 +33,8 @@ abstract class CommonIntroController extends GetxController {
bool get hasCoin => coinNum.value != 0;
// 是否收藏
final RxBool hasFav = false.obs;
// 是否稍后再看
final RxBool hasLater = false.obs;
final Rx<List<VideoTagItem>?> videoTags = Rx<List<VideoTagItem>?>(null);
@@ -39,6 +47,62 @@ abstract class CommonIntroController extends GetxController {
StatDetail? getStat();
final Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
Future<void> queryVideoIntro();
bool prevPlay();
bool nextPlay();
// 同时观看
final bool isShowOnlineTotal = Pref.enableOnlineTotal;
late final RxString total = '1'.obs;
Timer? timer;
final RxInt cid = int.parse(Get.parameters['cid']!).obs;
@override
void onInit() {
super.onInit();
queryVideoIntro();
startTimer();
}
void startTimer() {
if (isShowOnlineTotal) {
queryOnlineTotal();
timer ??= Timer.periodic(const Duration(seconds: 10), (Timer timer) {
queryOnlineTotal();
});
}
}
void canelTimer() {
timer?.cancel();
timer = null;
}
// 查看同时在看人数
Future<void> queryOnlineTotal() async {
if (!isShowOnlineTotal) {
return;
}
var result = await VideoHttp.onlineTotal(
aid: IdUtils.bv2av(bvid),
bvid: bvid,
cid: cid.value,
);
if (result['status']) {
total.value = result['data'];
}
}
@override
void onClose() {
canelTimer();
super.onClose();
}
Future<LoadingState<FavFolderData>> queryVideoInFolder() async {
favIds = null;
final (rid, type) = getFavRidType();
@@ -197,4 +261,12 @@ abstract class CommonIntroController extends GetxController {
videoTags.value = null;
}
}
Future<void> viewLater() async {
var res = await (hasLater.value
? UserHttp.toViewDel(aids: [IdUtils.bv2av(bvid)])
: await UserHttp.toViewLater(bvid: bvid));
if (res['status']) hasLater.value = !hasLater.value;
SmartDialog.showToast(res['msg']);
}
}

View File

@@ -41,7 +41,7 @@ class EpisodePanel extends CommonSlidePage {
const EpisodePanel({
super.key,
super.enableSlide,
required this.videoIntroController,
required this.ugcIntroController,
required this.heroTag,
required this.type,
// required this.count,
@@ -57,11 +57,11 @@ class EpisodePanel extends CommonSlidePage {
this.isSupportReverse,
this.isReversed,
this.onReverse,
required this.changeFucCall,
required this.onChangeEpisode,
this.onClose,
});
}) : assert(type == EpisodeType.pgc || ugcIntroController != null);
final VideoIntroController videoIntroController;
final UgcIntroController? ugcIntroController;
final String heroTag;
final EpisodeType type;
// final int count;
@@ -76,7 +76,7 @@ class EpisodePanel extends CommonSlidePage {
final int initialTabIndex;
final bool? isSupportReverse;
final bool? isReversed;
final Function changeFucCall;
final Function onChangeEpisode;
final VoidCallback? onReverse;
final VoidCallback? onClose;
@@ -315,7 +315,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
: episode.pages,
cover: episode.arc?.pic,
heroTag: widget.heroTag,
videoIntroController: widget.videoIntroController,
ugcIntroController: widget.ugcIntroController!,
bvid: IdUtils.av2bv(episode.aid),
),
),
@@ -401,7 +401,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
_currentItemIndex = index;
}
final isEpisode = episode is ugc.BaseEpisodeItem;
widget.changeFucCall(
widget.onChangeEpisode(
episode is pgc.EpisodeItem ? episode.epId : null,
isEpisode ? episode.bvid : widget.bvid,
episode.cid,
@@ -411,7 +411,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
if (widget.type == EpisodeType.season) {
try {
Get.find<VideoDetailController>(
tag: widget.videoIntroController.heroTag,
tag: widget.ugcIntroController!.heroTag,
).seasonCid = episode.cid;
} catch (_) {}
}

View File

@@ -89,7 +89,7 @@ class _SavePanelState extends State<SavePanel> {
if (currentRoute.startsWith('/video')) {
try {
final heroTag = Get.arguments?['heroTag'];
late final ctr = Get.find<VideoIntroController>(tag: heroTag);
late final ctr = Get.find<UgcIntroController>(tag: heroTag);
final videoDetail = ctr.videoDetail.value;
cover = videoDetail.pic;
title = videoDetail.title;

View File

@@ -19,6 +19,14 @@ class AiConclusionPanel extends CommonCollapseSlidePage {
}
class _AiDetailState extends CommonCollapseSlidePageState<AiConclusionPanel> {
final _controller = ScrollController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget buildPage(ThemeData theme) {
return Material(
@@ -53,7 +61,7 @@ class _AiDetailState extends CommonCollapseSlidePageState<AiConclusionPanel> {
@override
Widget buildList(ThemeData theme) {
return CustomScrollView(
controller: ScrollController(),
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
if (widget.item.summary?.isNotEmpty == true) ...[

View File

@@ -68,11 +68,11 @@ class VideoDetailController extends GetxController
/// 路由传参
String bvid = Get.parameters['bvid']!;
final RxInt cid = int.parse(Get.parameters['cid']!).obs;
final RxInt danmakuCid = 0.obs;
final heroTag = Get.arguments['heroTag'];
final RxString cover = ''.obs;
// 视频类型 默认投稿视频
final SearchType videoType = Get.arguments['videoType'] ?? SearchType.video;
final isUgc =
(Get.arguments['videoType'] ?? SearchType.video) == SearchType.video;
/// tabs相关配置
late TabController tabCtr;
@@ -114,7 +114,7 @@ class VideoDetailController extends GetxController
late String cacheDecode;
late String cacheSecondDecode;
bool get showReply => videoType == SearchType.video
bool get showReply => isUgc
? plPlayerController.showVideoReply
: plPlayerController.showBangumiReply;
@@ -283,7 +283,6 @@ class VideoDetailController extends GetxController
);
autoPlay.value = Pref.autoPlayEnable;
if (autoPlay.value) isShowCover.value = false;
danmakuCid.value = cid.value;
// 预设的解码格式
cacheDecode = Pref.defaultDecode;
@@ -336,9 +335,9 @@ class VideoDetailController extends GetxController
try {
for (var item in mediaList) {
if (item.cid != null) {
Get.find<VideoIntroController>(
Get.find<UgcIntroController>(
tag: heroTag,
).changeSeasonOrbangu(
).onChangeEpisode(
null,
item.bvid,
item.cid,
@@ -367,9 +366,9 @@ class VideoDetailController extends GetxController
mediaList: mediaList,
changeMediaList: (bvid, cid, aid, cover) {
try {
Get.find<VideoIntroController>(
Get.find<UgcIntroController>(
tag: heroTag,
).changeSeasonOrbangu(null, bvid, cid, aid, cover);
).onChangeEpisode(null, bvid, cid, aid, cover);
} catch (_) {}
},
panelTitle: watchLaterTitle,
@@ -901,11 +900,11 @@ class VideoDetailController extends GetxController
onTap: (_) {
if (item is int) {
try {
VideoIntroController videoIntroController =
Get.find<VideoIntroController>(tag: heroTag);
UgcIntroController ugcIntroController =
Get.find<UgcIntroController>(tag: heroTag);
Part part =
videoIntroController.videoDetail.value.pages![item];
videoIntroController.changeSeasonOrbangu(
ugcIntroController.videoDetail.value.pages![item];
ugcIntroController.onChangeEpisode(
null,
bvid,
part.cid,
@@ -1137,9 +1136,9 @@ class VideoDetailController extends GetxController
bvid: bvid,
cid: cid.value,
autoplay: autoplay ?? autoPlay.value,
epid: videoType == SearchType.media_bangumi ? epId : null,
seasonId: videoType == SearchType.media_bangumi ? seasonId : null,
subType: videoType == SearchType.media_bangumi ? subType : null,
epid: isUgc ? null : epId,
seasonId: isUgc ? null : seasonId,
subType: isUgc ? null : subType,
callback: () {
if (videoState.value is! Success) {
videoState.value = const Success(null);
@@ -1496,9 +1495,9 @@ class VideoDetailController extends GetxController
if (res['status']) {
PlayInfoData playInfo = res['data'];
// interactive video
if (graphVersion == null) {
if (isUgc && graphVersion == null) {
try {
final introCtr = Get.find<VideoIntroController>(tag: heroTag);
final introCtr = Get.find<UgcIntroController>(tag: heroTag);
if (introCtr.videoDetail.value.rights?.isSteinGate == 1) {
graphVersion = playInfo.interaction?.graphVersion;
getSteinEdgeInfo();
@@ -1508,16 +1507,17 @@ class VideoDetailController extends GetxController
}
}
if (continuePlayingPart) {
if (isUgc && continuePlayingPart) {
continuePlayingPart = false;
try {
VideoIntroController videoIntroController =
Get.find<VideoIntroController>(tag: heroTag);
if ((videoIntroController.videoDetail.value.pages?.length ?? 0) > 1 &&
UgcIntroController ugcIntroController = Get.find<UgcIntroController>(
tag: heroTag,
);
if ((ugcIntroController.videoDetail.value.pages?.length ?? 0) > 1 &&
playInfo.lastPlayCid != null &&
playInfo.lastPlayCid != 0) {
if (playInfo.lastPlayCid != cid.value) {
int index = videoIntroController.videoDetail.value.pages!
int index = ugcIntroController.videoDetail.value.pages!
.indexWhere((item) => item.cid == playInfo.lastPlayCid);
if (index != -1) {
onAddItem(index);
@@ -1598,9 +1598,9 @@ class VideoDetailController extends GetxController
isManual: true,
bvid: bvid,
cid: cid.value,
epid: videoType == SearchType.media_bangumi ? epId : null,
seasonId: videoType == SearchType.media_bangumi ? seasonId : null,
subType: videoType == SearchType.media_bangumi ? subType : null,
epid: isUgc ? null : epId,
seasonId: isUgc ? null : seasonId,
subType: isUgc ? null : subType,
);
} catch (_) {}
}
@@ -1683,7 +1683,7 @@ class VideoDetailController extends GetxController
void showNoteList(BuildContext context) {
String? title;
try {
title = Get.find<VideoIntroController>(
title = Get.find<UgcIntroController>(
tag: heroTag,
).videoDetail.value.title;
} catch (_) {}

View File

@@ -10,14 +10,15 @@ import 'package:PiliPlus/models/pgc_lcf.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart';
import 'package:PiliPlus/models_new/triple/pgc_triple.dart';
import 'package:PiliPlus/models_new/video/video_detail/data.dart';
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
import 'package:PiliPlus/pages/video/controller.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
import 'package:PiliPlus/pages/video/pay_coins/view.dart';
import 'package:PiliPlus/pages/video/reply/controller.dart';
import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/page_utils.dart';
@@ -257,7 +258,7 @@ class PgcIntroController extends CommonIntroController {
}
// 修改分P或番剧分集
void changeSeasonOrbangu(dynamic epId, bvid, cid, aid, cover) {
void onChangeEpisode(dynamic epId, bvid, cid, aid, cover) {
// 重新获取视频资源
this.epId = epId;
this.bvid = bvid;
@@ -270,7 +271,6 @@ class PgcIntroController extends CommonIntroController {
..epId = epId
..bvid = bvid
..cid.value = cid
..danmakuCid.value = cid
..queryVideoUrl();
if (cover is String && cover.isNotEmpty) {
videoDetailCtr.cover.value = cover;
@@ -289,13 +289,9 @@ class PgcIntroController extends CommonIntroController {
queryPgcLikeCoinFav();
}
try {
Get.find<VideoIntroController>(tag: Get.arguments['heroTag'])
..bvid = bvid
..lastPlayCid.value = cid
..queryVideoIntro()
..queryOnlineTotal();
} catch (_) {}
this.cid.value = cid;
queryVideoIntro();
queryOnlineTotal();
}
// 追番
@@ -328,6 +324,7 @@ class PgcIntroController extends CommonIntroController {
SmartDialog.showToast(result['msg']);
}
@override
bool prevPlay() {
final episodes = pgcItem.episodes!;
VideoDetailController videoDetailCtr = Get.find<VideoDetailController>(
@@ -346,7 +343,7 @@ class PgcIntroController extends CommonIntroController {
}
}
final episode = episodes[prevIndex];
changeSeasonOrbangu(
onChangeEpisode(
episode.epId,
episode.bvid,
episode.cid,
@@ -357,6 +354,7 @@ class PgcIntroController extends CommonIntroController {
}
/// 列表循环或者顺序播放时,自动播放下一个;自动连播时,播放相关视频
@override
bool nextPlay() {
try {
final episodes = pgcItem.episodes!;
@@ -380,7 +378,7 @@ class PgcIntroController extends CommonIntroController {
}
}
final episode = episodes[nextIndex];
changeSeasonOrbangu(
onChangeEpisode(
episode.epId,
episode.bvid,
episode.cid,
@@ -456,4 +454,14 @@ class PgcIntroController extends CommonIntroController {
}
});
}
@override
Future<void> queryVideoIntro() async {
var res = await VideoHttp.videoIntro(bvid: bvid);
if (res.isSuccess) {
VideoDetailData data = res.data;
videoPlayerServiceHandler.onVideoDetailChange(data, data.cid!, heroTag);
videoDetail.value = data;
}
}
}

View File

@@ -252,7 +252,7 @@ class _PgcIntroPageState extends State<PgcIntroPage>
heroTag: widget.heroTag,
pages: item.episodes!,
cid: videoDetailCtr.cid.value,
changeFuc: pgcIntroController.changeSeasonOrbangu,
onChangeEpisode: pgcIntroController.onChangeEpisode,
showEpisodes: widget.showEpisodes,
newEp: item.newEp,
),
@@ -326,13 +326,15 @@ class _PgcIntroPageState extends State<PgcIntroPage>
needAnim: true,
),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.comment),
selectIcon: const Icon(FontAwesomeIcons.reply),
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
semanticsLabel: '评论',
text: NumUtil.numFormat(item.stat!.reply),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.clock),
selectIcon: const Icon(FontAwesomeIcons.solidClock),
onTap: () => handleState(pgcIntroController.viewLater),
selectStatus: pgcIntroController.hasLater.value,
semanticsLabel: '再看',
text: '再看',
),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),

View File

@@ -94,94 +94,99 @@ class _IntroDetailState extends CommonCollapseSlidePageState<PgcIntroPanel> {
fontSize: 12,
color: theme.colorScheme.onSurface,
);
return ListView(
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.only(
left: 14,
right: 14,
top: 14,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
children: [
SelectableText(
widget.item.title!,
style: const TextStyle(fontSize: 16),
final TextStyle textStyle = TextStyle(
color: theme.colorScheme.onSurfaceVariant,
);
return SelectionArea(
child: ListView(
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.only(
left: 14,
right: 14,
top: 14,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
const SizedBox(height: 4),
Row(
spacing: 6,
children: [
StatWidget(
type: StatType.play,
value: widget.item.stat!.view,
),
StatWidget(
type: StatType.danmaku,
value: widget.item.stat!.danmaku,
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.item.areas!.first.name!,
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.item.publish!.pubTimeShow!,
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.item.newEp!.desc!,
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
widget.item.evaluate!,
style: smallTitle.copyWith(fontSize: 14),
),
const SizedBox(height: 20),
Text(
'声优:',
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
widget.item.actors!,
style: smallTitle.copyWith(fontSize: 14),
),
if (widget.videoTags?.isNotEmpty == true) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: widget.videoTags!
.map(
(item) => SearchText(
fontSize: 13,
text: item.tagName!,
onTap: (tagName) => Get.toNamed(
'/searchResult',
parameters: {'keyword': tagName},
),
onLongPress: Utils.copyText,
),
)
.toList(),
children: [
Text(
widget.item.title!,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 4),
Row(
spacing: 6,
children: [
StatWidget(
type: StatType.play,
value: widget.item.stat!.view,
),
StatWidget(
type: StatType.danmaku,
value: widget.item.stat!.danmaku,
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.item.areas!.first.name!,
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.item.publish!.pubTimeShow!,
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.item.newEp!.desc!,
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
widget.item.evaluate!,
style: textStyle,
),
const SizedBox(height: 20),
Text(
'演职人员:',
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
widget.item.actors!,
style: textStyle,
),
if (widget.videoTags?.isNotEmpty == true) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: widget.videoTags!
.map(
(item) => SearchText(
fontSize: 13,
text: item.tagName!,
onTap: (tagName) => Get.toNamed(
'/searchResult',
parameters: {'keyword': tagName},
),
onLongPress: Utils.copyText,
),
)
.toList(),
),
],
],
],
),
);
}
}

View File

@@ -15,7 +15,7 @@ class PgcPanel extends StatefulWidget {
super.key,
required this.pages,
this.cid,
required this.changeFuc,
required this.onChangeEpisode,
required this.showEpisodes,
required this.heroTag,
this.newEp,
@@ -23,7 +23,7 @@ class PgcPanel extends StatefulWidget {
final List<EpisodeItem> pages;
final int? cid;
final Function changeFuc;
final Function onChangeEpisode;
final Function showEpisodes;
final String heroTag;
final NewEp? newEp;
@@ -154,7 +154,7 @@ class _PgcPanelState extends State<PgcPanel> {
vipStatus != 1) {
SmartDialog.showToast('需要大会员');
}
widget.changeFuc(
widget.onChangeEpisode(
item.epId,
item.bvid,
item.cid,

View File

@@ -43,13 +43,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class VideoIntroController extends CommonIntroController with ReloadMixin {
String heroTag = '';
class UgcIntroController extends CommonIntroController with ReloadMixin {
late ExpandableController expandableCtr;
final RxBool status = true.obs;
final Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
// up主粉丝数
final Rx<MemberCardInfoData> userStat = MemberCardInfoData().obs;
@@ -60,16 +57,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
// 是否点踩
final RxBool hasDislike = false.obs;
// 是否稍后再看
final RxBool hasLater = false.obs;
final RxInt lastPlayCid = 0.obs;
// 同时观看
final bool isShowOnlineTotal = Pref.enableOnlineTotal;
late final RxString total = '1'.obs;
Timer? timer;
late final showArgueMsg = Pref.showArgueMsg;
late final enableAi = Pref.enableAi;
late final horizontalMemberPage = Pref.horizontalMemberPage;
@@ -92,11 +79,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
});
}
try {
if (heroTag.isEmpty) {
heroTag = Get.arguments['heroTag'];
}
} catch (_) {}
try {
var videoItem = Get.arguments['videoItem'];
if (videoItem != null) {
@@ -110,12 +92,10 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
}
}
} catch (_) {}
lastPlayCid.value = int.parse(Get.parameters['cid']!);
startTimer();
queryVideoIntro();
}
// 获取视频简介&分p
@override
Future<void> queryVideoIntro() async {
queryVideoTags();
var res = await VideoHttp.videoIntro(bvid: bvid);
@@ -152,8 +132,8 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
}
} catch (_) {}
final pages = videoDetail.value.pages;
if (pages != null && pages.isNotEmpty && lastPlayCid.value == 0) {
lastPlayCid.value = pages.first.cid!;
if (pages != null && pages.isNotEmpty && cid.value == 0) {
cid.value = pages.first.cid!;
}
queryUserStat(data.staff);
} else {
@@ -298,14 +278,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
}
}
Future<void> viewLater() async {
var res = await (hasLater.value
? UserHttp.toViewDel(aids: [IdUtils.bv2av(bvid)])
: await UserHttp.toViewLater(bvid: bvid));
if (res['status']) hasLater.value = !hasLater.value;
SmartDialog.showToast(res['msg']);
}
// 投币
Future<void> actionCoinVideo() async {
if (!accountService.isLogin.value) {
@@ -493,7 +465,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
}
// 修改分P或番剧分集
bool changeSeasonOrbangu(dynamic epid, bvid, cid, aid, cover, [isStein]) {
bool onChangeEpisode(dynamic epid, bvid, cid, aid, cover, [isStein]) {
// 重新获取视频资源
final videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
@@ -519,7 +491,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
..bvid = bvid
..oid.value = aid ?? IdUtils.bv2av(bvid)
..cid.value = cid
..danmakuCid.value = cid
..queryVideoUrl();
if (this.bvid != bvid) {
@@ -553,48 +524,19 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
queryVideoIntro();
}
lastPlayCid.value = cid;
this.cid.value = cid;
queryOnlineTotal();
return true;
}
void startTimer() {
if (isShowOnlineTotal) {
queryOnlineTotal();
timer ??= Timer.periodic(const Duration(seconds: 10), (Timer timer) {
queryOnlineTotal();
});
}
}
void canelTimer() {
timer?.cancel();
timer = null;
}
// 查看同时在看人数
Future<void> queryOnlineTotal() async {
if (!isShowOnlineTotal) {
return;
}
var result = await VideoHttp.onlineTotal(
aid: IdUtils.bv2av(bvid),
bvid: bvid,
cid: lastPlayCid.value,
);
if (result['status']) {
total.value = result['data'];
}
}
@override
void onClose() {
canelTimer();
expandableCtr.dispose();
super.onClose();
}
/// 播放上一个
@override
bool prevPlay([bool skipPages = false]) {
final List episodes = [];
bool isPages = false;
@@ -624,7 +566,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
? videoDetail.isPageReversed == true
? videoDetail.pages!.last.cid
: videoDetail.pages!.first.cid
: lastPlayCid.value),
: this.cid.value),
);
int prevIndex = currentIndex - 1;
final PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat;
@@ -644,11 +586,12 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
final int cid = episodes[prevIndex].cid!;
final String rBvid = isPages ? bvid : episodes[prevIndex].bvid;
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[prevIndex].aid!;
changeSeasonOrbangu(null, rBvid, cid, rAid, null);
onChangeEpisode(null, rBvid, cid, rAid, null);
return true;
}
/// 列表循环或者顺序播放时,自动播放下一个
@override
bool nextPlay([bool skipPages = false]) {
try {
final List episodes = [];
@@ -731,7 +674,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
final String rBvid = isPages ? bvid : episodes[nextIndex].bvid;
final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!;
changeSeasonOrbangu(null, rBvid, cid, rAid, null);
onChangeEpisode(null, rBvid, cid, rAid, null);
return true;
} catch (_) {
return false;
@@ -762,7 +705,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
final firstItem = relatedCtr.loadingState.value.data!.first;
try {
if (firstItem.cid != null) {
changeSeasonOrbangu(
onChangeEpisode(
null,
firstItem.bvid,
firstItem.cid,
@@ -771,7 +714,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
);
} else {
SearchHttp.ab2c(aid: firstItem.aid, bvid: firstItem.bvid).then(
(cid) => changeSeasonOrbangu(
(cid) => onChangeEpisode(
null,
firstItem.bvid,
cid,
@@ -791,7 +734,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
SmartDialog.showLoading(msg: '正在获取AI总结');
final res = await VideoHttp.aiConclusion(
bvid: bvid,
cid: lastPlayCid.value,
cid: cid.value,
upMid: videoDetail.value.owner?.mid,
);
SmartDialog.dismiss();

View File

@@ -35,8 +35,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class VideoIntroPanel extends StatefulWidget {
const VideoIntroPanel({
class UgcIntroPanel extends StatefulWidget {
const UgcIntroPanel({
super.key,
required this.heroTag,
required this.showAiBottomSheet,
@@ -49,12 +49,12 @@ class VideoIntroPanel extends StatefulWidget {
final ValueChanged<int?> onShowMemberPage;
@override
State<VideoIntroPanel> createState() => _VideoIntroPanelState();
State<UgcIntroPanel> createState() => _UgcIntroPanelState();
}
class _VideoIntroPanelState extends State<VideoIntroPanel>
class _UgcIntroPanelState extends State<UgcIntroPanel>
with AutomaticKeepAliveClientMixin {
late VideoIntroController introController;
late UgcIntroController ugcIntroController;
late final VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: widget.heroTag);
@@ -66,7 +66,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
@override
void initState() {
super.initState();
introController = Get.put(VideoIntroController(), tag: widget.heroTag)
ugcIntroController = Get.put(UgcIntroController(), tag: widget.heroTag)
..heroTag = widget.heroTag;
}
@@ -96,7 +96,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
),
sliver: Obx(
() {
VideoDetailData videoDetail = introController.videoDetail.value;
VideoDetailData videoDetail =
ugcIntroController.videoDetail.value;
bool isLoading = videoDetail.bvid == null;
int? mid = videoDetail.owner?.mid;
return SliverToBoxAdapter(
@@ -107,7 +108,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
return;
}
feedBack();
introController.expandableCtr.toggle();
ugcIntroController.expandableCtr.toggle();
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -127,7 +128,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
if (mid != null) {
feedBack();
if (!isPortrait &&
introController
ugcIntroController
.horizontalMemberPage) {
widget.onShowMemberPage(mid);
} else {
@@ -146,7 +147,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: ReloadScrollPhysics(
controller: introController,
controller: ugcIntroController,
),
child: Row(
spacing: 25,
@@ -170,7 +171,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
context,
isLoading,
videoDetail,
introController,
ugcIntroController,
),
),
],
@@ -182,7 +183,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
_buildVideoTitle(theme, videoDetail)
else
ExpandablePanel(
controller: introController.expandableCtr,
controller: ugcIntroController.expandableCtr,
collapsed: GestureDetector(
onLongPress: () {
Feedback.forLongPress(context);
@@ -208,11 +209,11 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
clipBehavior: Clip.none,
children: [
_buildInfo(theme, videoDetail),
if (introController.enableAi) _aiBtn,
if (ugcIntroController.enableAi) _aiBtn,
],
),
if (videoDetail.argueInfo?.argueMsg?.isNotEmpty == true &&
introController.showArgueMsg) ...[
ugcIntroController.showArgueMsg) ...[
const SizedBox(height: 2),
Text.rich(
TextSpan(
@@ -238,7 +239,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
),
],
ExpandablePanel(
controller: introController.expandableCtr,
controller: ugcIntroController.expandableCtr,
collapsed: const SizedBox.shrink(),
expanded: Column(
mainAxisSize: MainAxisSize.min,
@@ -270,7 +271,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
),
],
Obx(() {
final videoTags = introController.videoTags.value;
final videoTags =
ugcIntroController.videoTags.value;
if (videoTags.isNullOrEmpty) {
return const SizedBox.shrink();
}
@@ -281,14 +283,15 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
theme: expandTheme,
),
Obx(
() => introController.status.value
() => ugcIntroController.status.value
? const SizedBox.shrink()
: Center(
child: TextButton.icon(
icon: const Icon(Icons.refresh),
onPressed: () {
introController.status.value = true;
introController.queryVideoIntro();
ugcIntroController
..status.value = true
..queryVideoIntro();
if (videoDetailCtr.videoUrl.isNullOrEmpty &&
!videoDetailCtr.isQuerying) {
videoDetailCtr.queryVideoUrl();
@@ -305,7 +308,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
context,
isLoading,
videoDetail,
introController,
ugcIntroController,
),
],
// 合集
@@ -317,9 +320,9 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
.horizontalSeasonPanel))
SeasonPanel(
heroTag: widget.heroTag,
changeFuc: introController.changeSeasonOrbangu,
onChangeEpisode: ugcIntroController.onChangeEpisode,
showEpisodes: widget.showEpisodes,
videoIntroController: introController,
ugcIntroController: ugcIntroController,
),
if (!isLoading &&
videoDetail.pages != null &&
@@ -330,8 +333,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
.horizontalSeasonPanel)) ...[
PagesPanel(
heroTag: widget.heroTag,
videoIntroController: introController,
bvid: introController.bvid,
ugcIntroController: ugcIntroController,
bvid: ugcIntroController.bvid,
showEpisodes: widget.showEpisodes,
),
],
@@ -484,9 +487,9 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
Widget followButton(BuildContext context, ThemeData t) {
return Obx(
() {
int attr = introController.followStatus['attribute'] ?? 0;
int attr = ugcIntroController.followStatus['attribute'] ?? 0;
return TextButton(
onPressed: () => introController.actionRelationMod(context),
onPressed: () => ugcIntroController.actionRelationMod(context),
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity(vertical: -2.8),
@@ -517,7 +520,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
BuildContext context,
bool isLoading,
VideoDetailData videoDetail,
VideoIntroController videoIntroController,
UgcIntroController ugcIntroController,
) {
return SizedBox(
height: 48,
@@ -527,19 +530,18 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
onLongPress: () =>
handleState(videoIntroController.actionOneThree),
selectStatus: videoIntroController.hasLike.value,
onTap: () => handleState(ugcIntroController.actionLikeVideo),
onLongPress: () => handleState(ugcIntroController.actionOneThree),
selectStatus: ugcIntroController.hasLike.value,
semanticsLabel: '点赞',
text: !isLoading
? NumUtil.numFormat(videoDetail.stat!.like)
: null,
needAnim: true,
hasTriple:
videoIntroController.hasLike.value &&
videoIntroController.hasCoin &&
videoIntroController.hasFav.value,
ugcIntroController.hasLike.value &&
ugcIntroController.hasCoin &&
ugcIntroController.hasFav.value,
callBack: (start) {
if (start) {
HapticFeedback.lightImpact();
@@ -556,8 +558,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsDown),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
onTap: () => handleState(videoIntroController.actionDislikeVideo),
selectStatus: videoIntroController.hasDislike.value,
onTap: () => handleState(ugcIntroController.actionDislikeVideo),
selectStatus: ugcIntroController.hasDislike.value,
semanticsLabel: '点踩',
text: "点踩",
),
@@ -567,8 +569,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
key: _coinKey,
icon: const Icon(FontAwesomeIcons.b),
selectIcon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
onTap: () => handleState(ugcIntroController.actionCoinVideo),
selectStatus: ugcIntroController.hasCoin,
semanticsLabel: '投币',
text: !isLoading
? NumUtil.numFormat(videoDetail.stat!.coin)
@@ -581,12 +583,12 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
key: _favKey,
icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController.showFavBottomSheet(
onTap: () => ugcIntroController.showFavBottomSheet(context),
onLongPress: () => ugcIntroController.showFavBottomSheet(
context,
isLongPress: true,
),
selectStatus: videoIntroController.hasFav.value,
selectStatus: ugcIntroController.hasFav.value,
semanticsLabel: '收藏',
text: !isLoading
? NumUtil.numFormat(videoDetail.stat!.favorite)
@@ -598,15 +600,15 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
() => ActionItem(
icon: const Icon(FontAwesomeIcons.clock),
selectIcon: const Icon(FontAwesomeIcons.solidClock),
onTap: () => handleState(videoIntroController.viewLater),
selectStatus: videoIntroController.hasLater.value,
onTap: () => handleState(ugcIntroController.viewLater),
selectStatus: ugcIntroController.hasLater.value,
semanticsLabel: '再看',
text: '再看',
),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => videoIntroController.actionShareVideo(context),
onTap: () => ugcIntroController.actionShareVideo(context),
selectStatus: false,
semanticsLabel: '分享',
text: !isLoading
@@ -717,7 +719,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
onTap: () {
if (item.mid == ownerMid &&
!isPortrait &&
introController.horizontalMemberPage) {
ugcIntroController.horizontalMemberPage) {
widget.onShowMemberPage(ownerMid);
} else {
Get.toNamed(
@@ -761,8 +763,9 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
right: -6,
child: Obx(
() =>
introController.staffRelations['status'] == true &&
introController.staffRelations['${item.mid}'] == null
ugcIntroController.staffRelations['status'] == true &&
ugcIntroController.staffRelations['${item.mid}'] ==
null
? Material(
type: MaterialType.transparency,
shape: const CircleBorder(),
@@ -773,7 +776,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
mid: item.mid,
isFollow: false,
callback: (val) {
introController.staffRelations['${item.mid}'] =
ugcIntroController
.staffRelations['${item.mid}'] =
true;
},
),
@@ -834,7 +838,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
behavior: HitTestBehavior.opaque,
child: Obx(
() {
final userStat = introController.userStat.value;
final userStat = ugcIntroController.userStat.value;
final isVip = (userStat.card?.vip?.status ?? 0) > 0;
return Row(
mainAxisSize: MainAxisSize.min,
@@ -904,10 +908,10 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
color: theme.colorScheme.outline,
semanticLabel: '无痕',
),
if (introController.isShowOnlineTotal)
if (ugcIntroController.isShowOnlineTotal)
Obx(
() => Text(
'${introController.total.value}人在看',
'${ugcIntroController.total.value}人在看',
style: TextStyle(
fontSize: 12,
color: theme.colorScheme.outline,
@@ -926,15 +930,15 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
label: 'AI总结',
child: GestureDetector(
onTap: () async {
if (introController.aiConclusionResult == null) {
await introController.aiConclusion();
if (ugcIntroController.aiConclusionResult == null) {
await ugcIntroController.aiConclusion();
}
if (introController.aiConclusionResult == null) {
if (ugcIntroController.aiConclusionResult == null) {
return;
}
if (introController.aiConclusionResult!.summary?.isNotEmpty ==
if (ugcIntroController.aiConclusionResult!.summary?.isNotEmpty ==
true ||
introController.aiConclusionResult!.outline?.isNotEmpty ==
ugcIntroController.aiConclusionResult!.outline?.isNotEmpty ==
true) {
widget.showAiBottomSheet();
} else {

View File

@@ -16,7 +16,7 @@ class PagesPanel extends StatefulWidget {
required this.bvid,
required this.heroTag,
this.showEpisodes,
required this.videoIntroController,
required this.ugcIntroController,
});
final List<Part>? list;
@@ -25,7 +25,7 @@ class PagesPanel extends StatefulWidget {
final String bvid;
final String heroTag;
final Function? showEpisodes;
final VideoIntroController videoIntroController;
final UgcIntroController ugcIntroController;
@override
State<PagesPanel> createState() => _PagesPanelState();
@@ -39,7 +39,7 @@ class _PagesPanelState extends State<PagesPanel> {
StreamSubscription? _listener;
List<Part> get pages =>
widget.list ?? widget.videoIntroController.videoDetail.value.pages!;
widget.list ?? widget.ugcIntroController.videoDetail.value.pages!;
@override
void initState() {
@@ -48,7 +48,7 @@ class _PagesPanelState extends State<PagesPanel> {
tag: widget.heroTag,
);
if (widget.list == null) {
cid = widget.videoIntroController.lastPlayCid.value;
cid = widget.ugcIntroController.cid.value;
pageIndex = pages.indexWhere((Part e) => e.cid == cid);
_listener = _videoDetailController.cid.listen((int cid) {
this.cid = cid;
@@ -156,7 +156,7 @@ class _PagesPanelState extends State<PagesPanel> {
if (widget.showEpisodes == null) {
Get.back();
}
widget.videoIntroController.changeSeasonOrbangu(
widget.ugcIntroController.onChangeEpisode(
null,
widget.bvid,
item.cid,
@@ -165,7 +165,7 @@ class _PagesPanelState extends State<PagesPanel> {
);
if (widget.list != null &&
widget
.videoIntroController
.ugcIntroController
.videoDetail
.value
.ugcSeason !=

View File

@@ -11,17 +11,17 @@ import 'package:get/get.dart';
class SeasonPanel extends StatefulWidget {
const SeasonPanel({
super.key,
required this.changeFuc,
required this.onChangeEpisode,
required this.heroTag,
required this.showEpisodes,
this.onTap,
required this.videoIntroController,
required this.ugcIntroController,
});
final Function changeFuc;
final Function onChangeEpisode;
final String heroTag;
final Function showEpisodes;
final bool? onTap;
final VideoIntroController videoIntroController;
final UgcIntroController ugcIntroController;
@override
State<SeasonPanel> createState() => _SeasonPanelState();
@@ -33,9 +33,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
StreamSubscription? _listener;
List<EpisodeItem> episodes = <EpisodeItem>[];
VideoIntroController get videoIntroController => widget.videoIntroController;
UgcIntroController get ugcIntroController => widget.ugcIntroController;
VideoDetailData get videoDetail =>
widget.videoIntroController.videoDetail.value;
widget.ugcIntroController.videoDetail.value;
@override
void initState() {
@@ -44,13 +44,12 @@ class _SeasonPanelState extends State<SeasonPanel> {
tag: widget.heroTag,
);
_videoDetailController.seasonCid =
videoIntroController.lastPlayCid.value != 0
_videoDetailController.seasonCid = ugcIntroController.cid.value != 0
? (videoDetail.pages?.isNotEmpty == true
? videoDetail.isPageReversed
? videoDetail.pages!.last.cid
: videoDetail.pages!.first.cid
: videoIntroController.lastPlayCid.value)
: ugcIntroController.cid.value)
: videoDetail.isPageReversed
? videoDetail.pages!.last.cid
: videoDetail.pages!.first.cid;

View File

@@ -30,12 +30,12 @@ class HorizontalMemberPage extends StatefulWidget {
super.key,
required this.mid,
required this.videoDetailController,
required this.videoIntroController,
required this.ugcIntroController,
});
final dynamic mid;
final VideoDetailController videoDetailController;
final VideoIntroController videoIntroController;
final UgcIntroController ugcIntroController;
@override
State<HorizontalMemberPage> createState() => _HorizontalMemberPageState();
@@ -200,8 +200,8 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
videoItem: videoItem,
bvid: _bvid,
onTap: () {
final status = widget.videoIntroController
.changeSeasonOrbangu(
final status = widget.ugcIntroController
.onChangeEpisode(
null,
videoItem.bvid,
videoItem.cid,

View File

@@ -51,6 +51,14 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
double get currentPos =>
plPlayerController.position.value.inMilliseconds / 1000;
final _controller = ScrollController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget buildPage(ThemeData theme) {
return Scaffold(
@@ -104,7 +112,7 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
clipBehavior: Clip.none,
children: [
SingleChildScrollView(
controller: ScrollController(),
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.only(bottom: 88 + bottom),
child: Column(

View File

@@ -11,12 +11,12 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/models/common/episode_panel_type.dart';
import 'package:PiliPlus/models/common/search_type.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart' as pgc;
import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart';
import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
import 'package:PiliPlus/models_new/video/video_detail/page.dart';
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/danmaku/view.dart';
import 'package:PiliPlus/pages/episode_panel/view.dart';
import 'package:PiliPlus/pages/video/ai_conclusion/view.dart';
@@ -77,12 +77,18 @@ class VideoDetailPageV extends StatefulWidget {
class _VideoDetailPageVState extends State<VideoDetailPageV>
with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver {
late VideoDetailController videoDetailController;
late VideoReplyController _videoReplyController;
late final VideoDetailController videoDetailController;
late final VideoReplyController _videoReplyController;
PlPlayerController? plPlayerController;
late VideoIntroController videoIntroController;
late PgcIntroController pgcIntroController;
late final _introController = ScrollController();
late final CommonIntroController introController = videoDetailController.isUgc
? ugcIntroController
: pgcIntroController;
late final UgcIntroController ugcIntroController;
late final PgcIntroController pgcIntroController;
ScrollController? _introScrollController;
ScrollController get introScrollController =>
_introScrollController ??= ScrollController();
late String heroTag;
bool get autoExitFullscreen =>
@@ -100,7 +106,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
bool get _shouldShowSeasonPanel {
late final videoDetail = videoIntroController.videoDetail.value;
if (!videoDetailController.isUgc) {
return false;
}
late final videoDetail = ugcIntroController.videoDetail.value;
return videoDetailController.plPlayerController.horizontalSeasonPanel &&
(videoDetail.ugcSeason != null ||
((videoDetail.pages?.length ?? 0) > 1)) &&
@@ -135,8 +144,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
tag: heroTag,
);
}
videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
if (videoDetailController.videoType == SearchType.media_bangumi) {
if (videoDetailController.isUgc) {
ugcIntroController = Get.put(UgcIntroController(), tag: heroTag);
} else {
pgcIntroController = Get.put(PgcIntroController(), tag: heroTag);
}
@@ -180,7 +190,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
videoIntroController.startTimer();
introController.startTimer();
videoDetailController.plPlayerController.showDanmaku = true;
// 修复从后台恢复时全屏状态下屏幕方向错误的问题
@@ -200,7 +210,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
});
}
} else if (state == AppLifecycleState.paused) {
videoIntroController.canelTimer();
introController.canelTimer();
videoDetailController.plPlayerController.showDanmaku = false;
}
}
@@ -256,11 +266,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
/// 顺序播放 列表循环
if (plPlayerController!.playRepeat != PlayRepeat.pause &&
plPlayerController!.playRepeat != PlayRepeat.singleCycle) {
if (videoDetailController.isPlayAll ||
videoDetailController.videoType == SearchType.video) {
notExitFlag = videoIntroController.nextPlay();
} else if (videoDetailController.videoType ==
SearchType.media_bangumi) {
if (videoDetailController.isUgc) {
notExitFlag = ugcIntroController.nextPlay();
} else {
notExitFlag = pgcIntroController.nextPlay();
}
}
@@ -346,10 +354,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
ScreenBrightness.instance.resetApplicationScreenBrightness();
PlPlayerController.setPlayCallBack(null);
}
videoDetailController.positionSubscription?.cancel();
videoIntroController.canelTimer();
videoIntroController.videoDetail.close();
videoDetailController.cid.close();
videoDetailController
..positionSubscription?.cancel()
..cid.close();
if (videoDetailController.isUgc) {
ugcIntroController
..canelTimer()
..videoDetail.close();
} else {
pgcIntroController.canelTimer();
}
if (!videoDetailController.horizontalScreen) {
AutoOrientation.portraitUpMode();
}
@@ -359,14 +373,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
if (plPlayerController != null) {
videoDetailController.makeHeartBeat();
plPlayerController!.removeStatusLister(playerListener);
plPlayerController!.removePositionListener(positionListener);
plPlayerController!.dispose();
plPlayerController!
..removeStatusLister(playerListener)
..removePositionListener(positionListener)
..dispose();
} else {
PlPlayerController.updatePlayCount();
}
VideoDetailPageV.routeObserver.unsubscribe(this);
showStatusBar();
_introScrollController?.dispose();
super.dispose();
}
@@ -382,7 +398,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
ScreenBrightness.instance.resetApplicationScreenBrightness();
videoDetailController.positionSubscription?.cancel();
videoIntroController.canelTimer();
introController.canelTimer();
videoDetailController
..playerStatus = plPlayerController?.playerStatus.status.value
@@ -416,7 +433,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
isShowing = true;
PlPlayerController.setPlayCallBack(playCallBack);
videoIntroController.startTimer();
introController.startTimer();
if (mounted) {
if (videoDetailController.brightness != null) {
plPlayerController?.setCurrBrightness(
@@ -769,11 +788,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
.colorScheme
.onSurface,
),
onSelected: (String type) async {
onSelected: (String type) {
switch (type) {
case 'later':
await videoIntroController
.viewLater();
introController.viewLater();
break;
case 'report':
if (!Accounts.main.isLogin) {
@@ -1127,7 +1145,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
buildTabbar(
introText: '相关视频',
showIntro:
videoDetailController.videoType == SearchType.video &&
videoDetailController.isUgc &&
videoDetailController
.plPlayerController
.showRelatedVideo,
@@ -1137,13 +1155,12 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
child: videoTabBarView(
controller: videoDetailController.tabCtr,
children: [
if (videoDetailController.videoType ==
SearchType.video &&
if (videoDetailController.isUgc &&
videoDetailController
.plPlayerController
.showRelatedVideo)
CustomScrollView(
controller: _introController,
controller: introScrollController,
slivers: [
RelatedVideoPanel(
key: relatedVideoPanelKey,
@@ -1215,14 +1232,12 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
key: Key(heroTag),
plPlayerController: plPlayerController!,
videoDetailController: videoDetailController,
videoIntroController:
videoDetailController.videoType == SearchType.video
? videoIntroController
: null,
pgcIntroController:
videoDetailController.videoType == SearchType.media_bangumi
? pgcIntroController
ugcIntroController: videoDetailController.isUgc
? ugcIntroController
: null,
pgcIntroController: videoDetailController.isUgc
? null
: pgcIntroController,
headerControl: HeaderControl(
controller: plPlayerController!,
videoDetailCtr: videoDetailController,
@@ -1232,11 +1247,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
? null
: Obx(
() => PlDanmaku(
key: Key(
videoDetailController.danmakuCid.value.toString(),
),
key: Key(videoDetailController.cid.value.toString()),
isPipMode: true,
cid: videoDetailController.danmakuCid.value,
cid: videoDetailController.cid.value,
playerController: plPlayerController!,
),
),
@@ -1321,10 +1334,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
),
],
),
onSelected: (String type) async {
onSelected: (String type) {
switch (type) {
case 'later':
await videoIntroController.viewLater();
introController.viewLater();
break;
case 'report':
if (!Accounts.main.isLogin) {
@@ -1400,14 +1413,12 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
key: Key(heroTag),
plPlayerController: plPlayerController!,
videoDetailController: videoDetailController,
videoIntroController:
videoDetailController.videoType == SearchType.video
? videoIntroController
: null,
pgcIntroController:
videoDetailController.videoType == SearchType.media_bangumi
? pgcIntroController
ugcIntroController: videoDetailController.isUgc
? ugcIntroController
: null,
pgcIntroController: videoDetailController.isUgc
? null
: pgcIntroController,
headerControl: HeaderControl(
key: videoDetailController.headerCtrKey,
controller: videoDetailController.plPlayerController,
@@ -1416,8 +1427,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
),
danmuWidget: Obx(
() => PlDanmaku(
key: Key(videoDetailController.danmakuCid.value.toString()),
cid: videoDetailController.danmakuCid.value,
key: Key(videoDetailController.cid.value.toString()),
cid: videoDetailController.cid.value,
playerController: plPlayerController!,
),
),
@@ -1507,7 +1518,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
String text = tabs[value];
if (text == '简介' || text == '相关视频') {
_introController.animToTop();
_introScrollController?.animToTop();
} else if (text.startsWith('评论')) {
_videoReplyController.animateToTop();
}
@@ -1759,7 +1770,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
MaterialTapTargetSize.shrinkWrap,
),
onPressed: () {
videoIntroController.changeSeasonOrbangu(
ugcIntroController.onChangeEpisode(
null,
videoDetailController.bvid,
item.cid,
@@ -1795,13 +1806,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
final bottom = MediaQuery.paddingOf(context).bottom;
Widget introPanel() => CustomScrollView(
key: const PageStorageKey<String>('简介'),
controller: needCtr ? _introController : null,
controller: needCtr ? introScrollController : null,
physics: !needCtr
? const AlwaysScrollableScrollPhysics(parent: ClampingScrollPhysics())
: null,
slivers: [
if (videoDetailController.videoType == SearchType.video) ...[
VideoIntroPanel(
if (videoDetailController.isUgc) ...[
UgcIntroPanel(
key: ugcPanelKey,
heroTag: heroTag,
showAiBottomSheet: showAiBottomSheet,
@@ -1828,7 +1839,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
height: bottom + StyleString.safeSpace,
),
),
] else if (videoDetailController.videoType == SearchType.media_bangumi)
] else
Obx(
() => PgcIntroPage(
key: pgcPanelKey,
@@ -1901,7 +1912,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
Widget get seasonPanel {
final videoDetail = videoIntroController.videoDetail.value;
final videoDetail = ugcIntroController.videoDetail.value;
return Column(
children: [
if ((videoDetail.pages?.length ?? 0) > 1)
@@ -1910,8 +1921,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
padding: const EdgeInsets.symmetric(horizontal: 14),
child: PagesPanel(
heroTag: heroTag,
videoIntroController: videoIntroController,
bvid: videoIntroController.bvid,
ugcIntroController: ugcIntroController,
bvid: ugcIntroController.bvid,
showEpisodes: showEpisodes,
),
)
@@ -1921,7 +1932,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
() => EpisodePanel(
heroTag: heroTag,
enableSlide: false,
videoIntroController: videoIntroController,
ugcIntroController: videoDetailController.isUgc
? ugcIntroController
: null,
type: EpisodeType.part,
list: [videoDetail.pages!],
cover: videoDetailController.cover.value,
@@ -1929,15 +1942,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
aid: IdUtils.bv2av(videoDetailController.bvid),
cid: videoDetailController.cid.value,
isReversed: videoDetail.isPageReversed,
changeFucCall:
videoDetailController.videoType ==
SearchType.media_bangumi
? pgcIntroController.changeSeasonOrbangu
: videoIntroController.changeSeasonOrbangu,
onChangeEpisode: videoDetailController.isUgc
? ugcIntroController.onChangeEpisode
: pgcIntroController.onChangeEpisode,
showTitle: false,
isSupportReverse:
videoDetailController.videoType !=
SearchType.media_bangumi,
isSupportReverse: videoDetailController.isUgc,
onReverse: () => onReversePlay(
bvid: videoDetailController.bvid,
aid: IdUtils.bv2av(videoDetailController.bvid),
@@ -1959,9 +1968,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
child: SeasonPanel(
heroTag: heroTag,
onTap: false,
changeFuc: videoIntroController.changeSeasonOrbangu,
onChangeEpisode: ugcIntroController.onChangeEpisode,
showEpisodes: showEpisodes,
videoIntroController: videoIntroController,
ugcIntroController: ugcIntroController,
),
),
Expanded(
@@ -1969,7 +1978,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
() => EpisodePanel(
heroTag: heroTag,
enableSlide: false,
videoIntroController: videoIntroController,
ugcIntroController: videoDetailController.isUgc
? ugcIntroController
: null,
type: EpisodeType.season,
initialTabIndex: videoDetailController.seasonIndex.value,
cover: videoDetailController.cover.value,
@@ -1978,19 +1989,17 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
bvid: videoDetailController.bvid,
aid: IdUtils.bv2av(videoDetailController.bvid),
cid: videoDetailController.seasonCid ?? 0,
isReversed: videoIntroController
isReversed: ugcIntroController
.videoDetail
.value
.ugcSeason!
.sections![videoDetailController.seasonIndex.value]
.isReversed,
changeFucCall:
videoDetailController.videoType == SearchType.media_bangumi
? pgcIntroController.changeSeasonOrbangu
: videoIntroController.changeSeasonOrbangu,
onChangeEpisode: videoDetailController.isUgc
? ugcIntroController.onChangeEpisode
: pgcIntroController.onChangeEpisode,
showTitle: false,
isSupportReverse:
videoDetailController.videoType != SearchType.media_bangumi,
isSupportReverse: videoDetailController.isUgc,
onReverse: () => onReversePlay(
bvid: videoDetailController.bvid,
aid: IdUtils.bv2av(videoDetailController.bvid),
@@ -2051,7 +2060,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
videoDetailController.childKey.currentState?.showBottomSheet(
backgroundColor: Colors.transparent,
(context) =>
AiConclusionPanel(item: videoIntroController.aiConclusionResult!),
AiConclusionPanel(item: ugcIntroController.aiConclusionResult!),
);
}
@@ -2075,7 +2084,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
Widget listSheetContent([bool? enableSlide]) => EpisodePanel(
heroTag: heroTag,
videoIntroController: videoIntroController,
ugcIntroController: videoDetailController.isUgc
? ugcIntroController
: null,
type: season != null
? EpisodeType.season
: episodes is List<Part>
@@ -2089,21 +2100,20 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
cid: cid,
seasonId: season?.id,
list: season != null ? season.sections : [episodes],
isReversed: videoDetailController.videoType == SearchType.media_bangumi
isReversed: !videoDetailController.isUgc
? null
: season != null
? videoIntroController
? ugcIntroController
.videoDetail
.value
.ugcSeason!
.sections![videoDetailController.seasonIndex.value]
.isReversed
: videoIntroController.videoDetail.value.isPageReversed,
isSupportReverse:
videoDetailController.videoType != SearchType.media_bangumi,
changeFucCall: videoDetailController.videoType == SearchType.media_bangumi
? pgcIntroController.changeSeasonOrbangu
: videoIntroController.changeSeasonOrbangu,
: ugcIntroController.videoDetail.value.isPageReversed,
isSupportReverse: videoDetailController.isUgc,
onChangeEpisode: videoDetailController.isUgc
? ugcIntroController.onChangeEpisode
: pgcIntroController.onChangeEpisode,
onClose: Get.back,
onReverse: () {
Get.back();
@@ -2143,10 +2153,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
return;
}
void changeEpisode(episode) {
void onChangeEpisode(episode) {
final isEpisode = episode is BaseEpisodeItem;
final isPgc = episode is pgc.EpisodeItem;
videoIntroController.changeSeasonOrbangu(
ugcIntroController.onChangeEpisode(
isPgc ? episode.epId : null,
isEpisode ? episode.bvid : bvid,
episode.cid,
@@ -2159,7 +2169,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
);
}
final videoDetail = videoIntroController.videoDetail.value;
final videoDetail = ugcIntroController.videoDetail.value;
if (isSeason) {
// reverse season
final item = videoDetail
@@ -2176,7 +2186,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
..cid.refresh();
} else {
// switch to first episode
var episode = videoIntroController
var episode = ugcIntroController
.videoDetail
.value
.ugcSeason!
@@ -2184,7 +2194,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
.episodes!
.first;
if (episode.cid != videoDetailController.cid.value) {
changeEpisode(episode);
onChangeEpisode(episode);
videoDetailController.seasonCid = episode.cid;
} else {
videoDetailController
@@ -2204,7 +2214,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
// switch to first episode
var episode = videoDetail.pages!.first;
if (episode.cid != videoDetailController.cid.value) {
changeEpisode(episode);
onChangeEpisode(episode);
} else {
videoDetailController.cid.refresh();
}
@@ -2264,7 +2274,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
return HorizontalMemberPage(
mid: mid,
videoDetailController: videoDetailController,
videoIntroController: videoIntroController,
ugcIntroController: ugcIntroController,
);
},
);

View File

@@ -32,6 +32,14 @@ class _ViewPointsPageState
int currentIndex = -1;
final _controller = ScrollController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget buildPage(ThemeData theme) {
return Scaffold(
@@ -92,7 +100,7 @@ class _ViewPointsPageState
color: theme.dividerColor.withValues(alpha: 0.1),
);
return ListView.separated(
controller: ScrollController(),
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom + 80,

View File

@@ -4,13 +4,13 @@ import 'dart:math';
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/models/common/search_type.dart';
import 'package:PiliPlus/models/common/super_resolution_type.dart';
import 'package:PiliPlus/models/common/video/audio_quality.dart';
import 'package:PiliPlus/models/common/video/cdn_type.dart';
import 'package:PiliPlus/models/common/video/video_decode_type.dart';
import 'package:PiliPlus/models/common/video/video_quality.dart';
import 'package:PiliPlus/models/video/play/url.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
import 'package:PiliPlus/pages/setting/widgets/switch_item.dart';
import 'package:PiliPlus/pages/video/controller.dart';
@@ -64,12 +64,17 @@ class HeaderControl extends StatefulWidget {
}
class HeaderControlState extends State<HeaderControl> {
PlayUrlModel get videoInfo => videoDetailCtr.data;
late final PlPlayerController plPlayerController = widget.controller;
late final VideoDetailController videoDetailCtr = widget.videoDetailCtr;
late final PlayUrlModel videoInfo = videoDetailCtr.data;
static const TextStyle subTitleStyle = TextStyle(fontSize: 12);
static const TextStyle titleStyle = TextStyle(fontSize: 14);
String get heroTag => widget.heroTag;
late VideoIntroController videoIntroController;
late PgcIntroController pgcIntroController;
late final UgcIntroController ugcIntroController;
late final PgcIntroController pgcIntroController;
late final CommonIntroController introController = videoDetailCtr.isUgc
? ugcIntroController
: pgcIntroController;
bool get horizontalScreen => videoDetailCtr.horizontalScreen;
RxString now = ''.obs;
Timer? clock;
@@ -78,14 +83,12 @@ class HeaderControlState extends State<HeaderControl> {
late final _coinKey = GlobalKey<ActionItemState>();
late final _favKey = GlobalKey<ActionItemState>();
PlPlayerController get plPlayerController => widget.controller;
VideoDetailController get videoDetailCtr => widget.videoDetailCtr;
@override
void initState() {
super.initState();
videoIntroController = Get.find<VideoIntroController>(tag: heroTag);
if (videoDetailCtr.videoType != SearchType.video) {
if (videoDetailCtr.isUgc) {
ugcIntroController = Get.find<UgcIntroController>(tag: heroTag);
} else {
pgcIntroController = Get.find<PgcIntroController>(tag: heroTag);
}
}
@@ -130,7 +133,7 @@ class HeaderControlState extends State<HeaderControl> {
dense: true,
onTap: () {
Get.back();
videoIntroController.viewLater();
introController.viewLater();
},
leading: const Icon(Icons.watch_later_outlined, size: 20),
title: const Text('添加至「稍后再看」', style: titleStyle),
@@ -874,7 +877,7 @@ class HeaderControlState extends State<HeaderControl> {
);
if (res.statusCode == 200) {
final name =
'${videoIntroController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json';
'${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json';
try {
DocumentFileSavePlusPlatform.instance
.saveMultipleFiles(
@@ -1897,7 +1900,7 @@ class HeaderControlState extends State<HeaderControl> {
},
),
),
if ((videoIntroController.videoDetail.value.title != null) &&
if ((introController.videoDetail.value.title != null) &&
(isFullScreen ||
(!isFullScreen &&
!horizontalScreen &&
@@ -1914,7 +1917,7 @@ class HeaderControlState extends State<HeaderControl> {
() {
String title;
final videoDetail =
videoIntroController.videoDetail.value;
introController.videoDetail.value;
if (videoDetail.videos == 1) {
title = videoDetail.title!;
} else {
@@ -1984,10 +1987,10 @@ class HeaderControlState extends State<HeaderControl> {
);
},
),
if (videoIntroController.isShowOnlineTotal)
if (introController.isShowOnlineTotal)
Obx(
() => Text(
'${videoIntroController.total.value}人正在看',
'${introController.total.value}人正在看',
style: const TextStyle(
color: Colors.white,
fontSize: 11,
@@ -2226,7 +2229,7 @@ class HeaderControlState extends State<HeaderControl> {
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (videoDetailCtr.videoType == SearchType.video) ...[
if (videoDetailCtr.isUgc) ...[
SizedBox(
width: 42,
height: 34,
@@ -2240,20 +2243,20 @@ class HeaderControlState extends State<HeaderControl> {
selectIcon: const Icon(
FontAwesomeIcons.solidThumbsUp,
),
onTap: videoIntroController.actionLikeVideo,
onTap: ugcIntroController.actionLikeVideo,
onLongPress: () {
videoIntroController.actionOneThree();
ugcIntroController.actionOneThree();
plPlayerController
..isTriple = null
..hideTaskControls();
},
selectStatus: videoIntroController.hasLike.value,
selectStatus: ugcIntroController.hasLike.value,
semanticsLabel: '点赞',
needAnim: true,
hasTriple:
videoIntroController.hasLike.value &&
videoIntroController.hasCoin &&
videoIntroController.hasFav.value,
ugcIntroController.hasLike.value &&
ugcIntroController.hasCoin &&
ugcIntroController.hasFav.value,
callBack: (start) {
if (start) {
HapticFeedback.lightImpact();
@@ -2284,8 +2287,8 @@ class HeaderControlState extends State<HeaderControl> {
selectIcon: const Icon(
FontAwesomeIcons.solidThumbsDown,
),
onTap: videoIntroController.actionDislikeVideo,
selectStatus: videoIntroController.hasDislike.value,
onTap: ugcIntroController.actionDislikeVideo,
selectStatus: ugcIntroController.hasDislike.value,
semanticsLabel: '点踩',
),
),
@@ -2302,8 +2305,8 @@ class HeaderControlState extends State<HeaderControl> {
color: Colors.white,
),
selectIcon: const Icon(FontAwesomeIcons.b),
onTap: videoIntroController.actionCoinVideo,
selectStatus: videoIntroController.hasCoin,
onTap: ugcIntroController.actionCoinVideo,
selectStatus: ugcIntroController.hasCoin,
semanticsLabel: '投币',
needAnim: true,
),
@@ -2321,11 +2324,11 @@ class HeaderControlState extends State<HeaderControl> {
color: Colors.white,
),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => videoIntroController
.showFavBottomSheet(context),
onLongPress: () => videoIntroController
onTap: () =>
ugcIntroController.showFavBottomSheet(context),
onLongPress: () => ugcIntroController
.showFavBottomSheet(context, isLongPress: true),
selectStatus: videoIntroController.hasFav.value,
selectStatus: ugcIntroController.hasFav.value,
semanticsLabel: '收藏',
needAnim: true,
),
@@ -2341,7 +2344,7 @@ class HeaderControlState extends State<HeaderControl> {
color: Colors.white,
),
onTap: () =>
videoIntroController.actionShareVideo(context),
ugcIntroController.actionShareVideo(context),
semanticsLabel: '分享',
),
),

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
import 'package:PiliPlus/models_new/video/video_detail/page.dart';
import 'package:PiliPlus/models_new/video/video_detail/section.dart';
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/video/controller.dart';
import 'package:PiliPlus/pages/video/introduction/pgc/controller.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
@@ -48,7 +49,7 @@ class PLVideoPlayer extends StatefulWidget {
const PLVideoPlayer({
required this.plPlayerController,
this.videoDetailController,
this.videoIntroController,
this.ugcIntroController,
this.pgcIntroController,
required this.headerControl,
this.bottomControl,
@@ -64,7 +65,7 @@ class PLVideoPlayer extends StatefulWidget {
final PlPlayerController plPlayerController;
final VideoDetailController? videoDetailController;
final VideoIntroController? videoIntroController;
final UgcIntroController? ugcIntroController;
final PgcIntroController? pgcIntroController;
final Widget headerControl;
final Widget? bottomControl;
@@ -86,8 +87,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
with TickerProviderStateMixin {
late AnimationController animationController;
late VideoController videoController;
VideoIntroController? videoIntroController;
UgcIntroController? ugcIntroController;
PgcIntroController? pgcIntroController;
late final CommonIntroController introController =
widget.videoDetailController!.isUgc
? ugcIntroController!
: pgcIntroController!;
final GlobalKey _playerKey = GlobalKey();
final GlobalKey<VideoState> key = GlobalKey<VideoState>();
@@ -170,7 +175,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
duration: const Duration(milliseconds: 100),
);
videoController = plPlayerController.videoController!;
videoIntroController = widget.videoIntroController;
ugcIntroController = widget.ugcIntroController;
pgcIntroController = widget.pgcIntroController;
Future.microtask(() async {
try {
@@ -250,9 +255,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
// 动态构建底部控制条
Widget buildBottomControl() {
final videoDetail = videoIntroController?.videoDetail.value;
bool isSeason = videoDetail?.ugcSeason != null;
bool isPage = videoDetail?.pages != null && videoDetail!.pages!.length > 1;
final videoDetail = introController.videoDetail.value;
bool isSeason = videoDetail.ugcSeason != null;
bool isPage = videoDetail.pages != null && videoDetail.pages!.length > 1;
bool isPgc = pgcIntroController != null;
bool anySeason = isSeason || isPage || isPgc;
double widgetWidth = isFullScreen && context.isLandscape ? 42 : 35;
@@ -270,14 +275,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
color: Colors.white,
),
onTap: () {
bool? res;
if (videoIntroController != null) {
res = videoIntroController!.prevPlay();
}
if (pgcIntroController != null) {
res = pgcIntroController!.prevPlay();
}
if (res == false) {
if (!introController.prevPlay()) {
SmartDialog.showToast('已经是第一集了');
}
},
@@ -302,14 +300,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
color: Colors.white,
),
onTap: () {
bool? res;
if (videoIntroController != null) {
res = videoIntroController!.nextPlay();
}
if (pgcIntroController != null) {
res = pgcIntroController!.nextPlay();
}
if (res == false) {
if (!introController.nextPlay()) {
SmartDialog.showToast('已经是最后一集了');
}
},
@@ -477,7 +468,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
int currentCid = plPlayerController.cid;
String bvid = plPlayerController.bvid;
List episodes = [];
final videoDetail = videoIntroController!.videoDetail.value;
final videoDetail = introController.videoDetail.value;
if (isSeason) {
final List<SectionItem> sections =
videoDetail.ugcSeason!.sections!;