feat: 播放顺序、视频详情操作栏样式

This commit is contained in:
guozhigq
2023-09-26 22:51:21 +08:00
parent 3f50aab12d
commit 26d8ab5b43
14 changed files with 314 additions and 50 deletions

View File

@ -9,6 +9,7 @@ import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
@ -21,7 +22,7 @@ class BangumiIntroController extends GetxController {
? int.parse(Get.parameters['seasonId']!) ? int.parse(Get.parameters['seasonId']!)
: null; : null;
var epId = Get.parameters['epId'] != null var epId = Get.parameters['epId'] != null
? int.parse(Get.parameters['epId']!) ? int.tryParse(Get.parameters['epId']!)
: null; : null;
// 是否预渲染 骨架屏 // 是否预渲染 骨架屏
@ -257,7 +258,7 @@ class BangumiIntroController extends GetxController {
VideoDetailController videoDetailCtr = VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']); Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
videoDetailCtr.bvid = bvid; videoDetailCtr.bvid = bvid;
videoDetailCtr.cid = cid; videoDetailCtr.cid.value = cid;
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
// 重新请求评论 // 重新请求评论
@ -292,4 +293,31 @@ class BangumiIntroController extends GetxController {
} }
return result; return result;
} }
/// 列表循环或者顺序播放时,自动播放下一个
void nextPlay() {
late List episodes;
if (bangumiDetail.value.episodes != null) {
episodes = bangumiDetail.value.episodes!;
}
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
int currentIndex =
episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value);
int nextIndex = currentIndex + 1;
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
// 列表循环
if (platRepeat == PlayRepeat.listCycle) {
if (nextIndex == episodes.length - 1) {
nextIndex = 0;
}
}
if (nextIndex <= episodes.length - 1 &&
platRepeat == PlayRepeat.listOrder) {}
int cid = episodes[nextIndex].cid!;
String bvid = episodes[nextIndex].bvid!;
int aid = episodes[nextIndex].aid!;
changeSeasonOrbangu(bvid, cid, aid);
}
} }

View File

@ -34,10 +34,12 @@ class BangumiIntroPanel extends StatefulWidget {
class _BangumiIntroPanelState extends State<BangumiIntroPanel> class _BangumiIntroPanelState extends State<BangumiIntroPanel>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
final BangumiIntroController bangumiIntroController = late BangumiIntroController bangumiIntroController;
Get.put(BangumiIntroController(), tag: Get.arguments['heroTag']); late VideoDetailController videoDetailCtr;
BangumiInfoModel? bangumiDetail; BangumiInfoModel? bangumiDetail;
late Future _futureBuilderFuture; late Future _futureBuilderFuture;
late int cid;
late String heroTag;
// 添加页面缓存 // 添加页面缓存
@override @override
@ -46,10 +48,19 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
heroTag = Get.arguments['heroTag'];
cid = widget.cid!;
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
bangumiIntroController.bangumiDetail.listen((value) { bangumiIntroController.bangumiDetail.listen((value) {
bangumiDetail = value; bangumiDetail = value;
}); });
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro(); _futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
videoDetailCtr.cid.listen((p0) {
print('🐶🐶$p0');
cid = p0;
setState(() {});
});
} }
@override @override
@ -61,9 +72,11 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data['status']) { if (snapshot.data['status']) {
// 请求成功 // 请求成功
return BangumiInfo( return BangumiInfo(
loadingStatus: false, loadingStatus: false,
bangumiDetail: bangumiDetail, bangumiDetail: bangumiDetail,
cid: cid,
); );
} else { } else {
// 请求错误 // 请求错误
@ -77,7 +90,7 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
return BangumiInfo( return BangumiInfo(
loadingStatus: true, loadingStatus: true,
bangumiDetail: bangumiDetail, bangumiDetail: bangumiDetail,
cid: widget.cid, cid: cid,
); );
} }
}, },
@ -118,6 +131,12 @@ class _BangumiInfoState extends State<BangumiInfo> {
bangumiItem = bangumiIntroController.bangumiItem; bangumiItem = bangumiIntroController.bangumiItem;
sheetHeight = localCache.get('sheetHeight'); sheetHeight = localCache.get('sheetHeight');
cid = widget.cid!; cid = widget.cid!;
print('cid: $cid');
videoDetailCtr.cid.listen((p0) {
cid = p0;
print('cid: $cid');
setState(() {});
});
} }
// 收藏 // 收藏

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
class BangumiPanel extends StatefulWidget { class BangumiPanel extends StatefulWidget {
@ -30,16 +32,28 @@ class _BangumiPanelState extends State<BangumiPanel> {
dynamic userInfo; dynamic userInfo;
// 默认未开通 // 默认未开通
int vipStatus = 0; int vipStatus = 0;
late int cid;
String heroTag = Get.arguments['heroTag'];
late final VideoDetailController videoDetailCtr;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!); cid = widget.cid!;
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
scrollToIndex(); scrollToIndex();
userInfo = userInfoCache.get('userInfoCache'); userInfo = userInfoCache.get('userInfoCache');
if (userInfo != null) { if (userInfo != null) {
vipStatus = userInfo.vipStatus; vipStatus = userInfo.vipStatus;
} }
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
videoDetailCtr.cid.listen((p0) {
cid = p0;
setState(() {});
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
scrollToIndex();
});
} }
@override @override

View File

@ -24,7 +24,7 @@ class VideoDetailController extends GetxController
with GetSingleTickerProviderStateMixin { with GetSingleTickerProviderStateMixin {
/// 路由传参 /// 路由传参
String bvid = Get.parameters['bvid']!; String bvid = Get.parameters['bvid']!;
int cid = int.parse(Get.parameters['cid']!); RxInt cid = int.parse(Get.parameters['cid']!).obs;
RxInt danmakuCid = 0.obs; RxInt danmakuCid = 0.obs;
String heroTag = Get.arguments['heroTag']; String heroTag = Get.arguments['heroTag'];
// 视频详情 // 视频详情
@ -109,7 +109,7 @@ class VideoDetailController extends GetxController
localCache.get(LocalCacheKey.historyPause) == true) { localCache.get(LocalCacheKey.historyPause) == true) {
enableHeart = false; enableHeart = false;
} }
danmakuCid.value = cid; danmakuCid.value = cid.value;
/// ///
if (Platform.isAndroid) { if (Platform.isAndroid) {
@ -218,7 +218,7 @@ class VideoDetailController extends GetxController
// 默认1倍速 // 默认1倍速
speed: 1.0, speed: 1.0,
bvid: bvid, bvid: bvid,
cid: cid, cid: cid.value,
enableHeart: enableHeart, enableHeart: enableHeart,
isFirstTime: isFirstTime, isFirstTime: isFirstTime,
autoplay: autoplay, autoplay: autoplay,
@ -230,7 +230,7 @@ class VideoDetailController extends GetxController
// 视频链接 // 视频链接
Future queryVideoUrl() async { Future queryVideoUrl() async {
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid); var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
if (result['status']) { if (result['status']) {
data = result['data']; data = result['data'];

View File

@ -11,6 +11,7 @@ import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
@ -58,6 +59,7 @@ class VideoIntroController extends GetxController {
RxString total = '1'.obs; RxString total = '1'.obs;
Timer? timer; Timer? timer;
bool isPaused = false; bool isPaused = false;
String heroTag = Get.arguments['heroTag'];
@override @override
void onInit() { void onInit() {
@ -102,9 +104,10 @@ class VideoIntroController extends GetxController {
if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) { if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) {
lastPlayCid.value = videoDetail.value.pages!.first.cid!; lastPlayCid.value = videoDetail.value.pages!.first.cid!;
} }
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']) // Get.find<VideoDetailController>(tag: heroTag).tabs.value = [
.tabs // '简介',
.value = ['简介', '评论 ${result['data']!.stat!.reply}']; // '评论 ${result['data']!.stat!.reply}'
// ];
// 获取到粉丝数再返回 // 获取到粉丝数再返回
await queryUserStat(); await queryUserStat();
} }
@ -440,16 +443,16 @@ class VideoIntroController extends GetxController {
Future changeSeasonOrbangu(bvid, cid, aid) async { Future changeSeasonOrbangu(bvid, cid, aid) async {
// 重新获取视频资源 // 重新获取视频资源
VideoDetailController videoDetailCtr = VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']); Get.find<VideoDetailController>(tag: heroTag);
videoDetailCtr.bvid = bvid; videoDetailCtr.bvid = bvid;
videoDetailCtr.cid = cid; videoDetailCtr.cid.value = cid;
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
// 重新请求评论 // 重新请求评论
try { try {
/// 未渲染回复组件时可能异常 /// 未渲染回复组件时可能异常
VideoReplyController videoReplyCtr = VideoReplyController videoReplyCtr =
Get.find<VideoReplyController>(tag: Get.arguments['heroTag']); Get.find<VideoReplyController>(tag: heroTag);
videoReplyCtr.aid = aid; videoReplyCtr.aid = aid;
videoReplyCtr.queryReplyList(type: 'init'); videoReplyCtr.queryReplyList(type: 'init');
} catch (_) {} } catch (_) {}
@ -486,4 +489,52 @@ class VideoIntroController extends GetxController {
} }
super.onClose(); super.onClose();
} }
/// 列表循环或者顺序播放时,自动播放下一个
void nextPlay() {
late List episodes;
// if (videoDetail.value.ugcSeason != null) {
// UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
// List<SectionItem> sections = ugcSeason.sections!;
// for (int i = 0; i < sections.length; i++) {
// List<EpisodeItem> episodesList = sections[i].episodes!;
// for (int j = 0; j < episodesList.length; j++) {
// if (episodesList[j].cid == lastPlayCid.value) {
// episodes = episodesList;
// continue;
// }
// }
// }
// }
if (videoDetail.value.ugcSeason != null) {
UgcSeason ugcSeason = videoDetail.value.ugcSeason!;
List<SectionItem> sections = ugcSeason.sections!;
episodes = [];
for (int i = 0; i < sections.length; i++) {
List<EpisodeItem> episodesList = sections[i].episodes!;
episodes.addAll(episodesList);
}
}
int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value);
int nextIndex = currentIndex + 1;
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: heroTag);
PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat;
// 列表循环
if (nextIndex >= episodes.length) {
if (platRepeat == PlayRepeat.listCycle) {
nextIndex = 0;
}
if (platRepeat == PlayRepeat.listOrder) {
return;
}
}
int cid = episodes[nextIndex].cid!;
String bvid = episodes[nextIndex].bvid!;
int aid = episodes[nextIndex].aid!;
changeSeasonOrbangu(bvid, cid, aid);
}
} }

View File

@ -330,17 +330,17 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
), ),
const SizedBox(height: 7), const SizedBox(height: 7),
// 点赞收藏转发 布局样式1 // 点赞收藏转发 布局样式1
SingleChildScrollView( // SingleChildScrollView(
padding: const EdgeInsets.only(top: 7, bottom: 7), // padding: const EdgeInsets.only(top: 7, bottom: 7),
scrollDirection: Axis.horizontal, // scrollDirection: Axis.horizontal,
child: actionRow( // child: actionRow(
context, // context,
videoIntroController, // videoIntroController,
videoDetailCtr, // videoDetailCtr,
), // ),
), // ),
// 点赞收藏转发 布局样式2 // 点赞收藏转发 布局样式2
// actionGrid(context, videoIntroController), actionGrid(context, videoIntroController),
// 合集 // 合集
if (!loadingStatus && if (!loadingStatus &&
widget.videoDetail!.ugcSeason != null) ...[ widget.videoDetail!.ugcSeason != null) ...[
@ -458,7 +458,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
Widget actionGrid(BuildContext context, videoIntroController) { Widget actionGrid(BuildContext context, videoIntroController) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
return Container( return Container(
padding: const EdgeInsets.only(top: 6, bottom: 10), margin: const EdgeInsets.only(top: 6, bottom: 4),
height: constraints.maxWidth / 5 * 0.8, height: constraints.maxWidth / 5 * 0.8,
child: GridView.count( child: GridView.count(
primary: false, primary: false,
@ -477,12 +477,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
? widget.videoDetail!.stat!.like!.toString() ? widget.videoDetail!.stat!.like!.toString()
: '-'), : '-'),
), ),
ActionItem( // ActionItem(
icon: const Icon(FontAwesomeIcons.clock), // icon: const Icon(FontAwesomeIcons.clock),
onTap: () => videoIntroController.actionShareVideo(), // onTap: () => videoIntroController.actionShareVideo(),
selectStatus: false, // selectStatus: false,
loadingStatus: loadingStatus, // loadingStatus: loadingStatus,
text: '稍后再看'), // text: '稍后再看'),
Obx( Obx(
() => ActionItem( () => ActionItem(
icon: const Icon(FontAwesomeIcons.b), icon: const Icon(FontAwesomeIcons.b),
@ -498,22 +498,28 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
() => ActionItem( () => ActionItem(
icon: const Icon(FontAwesomeIcons.star), icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar), selectIcon: const Icon(FontAwesomeIcons.solidStar),
// onTap: () => videoIntroController.actionFavVideo(),
onTap: () => showFavBottomSheet(), onTap: () => showFavBottomSheet(),
onLongPress: () => showFavBottomSheet(type: 'longPress'),
selectStatus: videoIntroController.hasFav.value, selectStatus: videoIntroController.hasFav.value,
loadingStatus: loadingStatus, loadingStatus: loadingStatus,
text: !loadingStatus text: !loadingStatus
? widget.videoDetail!.stat!.favorite!.toString() ? widget.videoDetail!.stat!.favorite!.toString()
: '-'), : '-'),
), ),
ActionItem(
icon: const Icon(FontAwesomeIcons.comment),
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
loadingStatus: loadingStatus,
text: !loadingStatus
? widget.videoDetail!.stat!.reply!.toString()
: '评论'),
ActionItem( ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare), icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => videoIntroController.actionShareVideo(), onTap: () => videoIntroController.actionShareVideo(),
selectStatus: false, selectStatus: false,
loadingStatus: loadingStatus, loadingStatus: loadingStatus,
text: !loadingStatus text: '分享'),
? widget.videoDetail!.stat!.share!.toString()
: '-'),
], ],
), ),
); );

View File

@ -6,6 +6,7 @@ class ActionItem extends StatelessWidget {
final Icon? icon; final Icon? icon;
final Icon? selectIcon; final Icon? selectIcon;
final Function? onTap; final Function? onTap;
final Function? onLongPress;
final bool? loadingStatus; final bool? loadingStatus;
final String? text; final String? text;
final bool selectStatus; final bool selectStatus;
@ -15,6 +16,7 @@ class ActionItem extends StatelessWidget {
this.icon, this.icon,
this.selectIcon, this.selectIcon,
this.onTap, this.onTap,
this.onLongPress,
this.loadingStatus, this.loadingStatus,
this.text, this.text,
this.selectStatus = false, this.selectStatus = false,
@ -27,6 +29,9 @@ class ActionItem extends StatelessWidget {
feedBack(), feedBack(),
onTap!(), onTap!(),
}, },
onLongPress: () => {
if (onLongPress != null) {onLongPress!()}
},
borderRadius: StyleString.mdRadius, borderRadius: StyleString.mdRadius,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart';
class PagesPanel extends StatefulWidget { class PagesPanel extends StatefulWidget {
final List<Part> pages; final List<Part> pages;
@ -22,13 +23,23 @@ class PagesPanel extends StatefulWidget {
class _PagesPanelState extends State<PagesPanel> { class _PagesPanelState extends State<PagesPanel> {
late List<Part> episodes; late List<Part> episodes;
late int cid;
late int currentIndex; late int currentIndex;
String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
cid = widget.cid!;
episodes = widget.pages; episodes = widget.pages;
currentIndex = episodes.indexWhere((e) => e.cid == widget.cid); _videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
currentIndex = episodes.indexWhere((e) => e.cid == cid);
_videoDetailController.cid.listen((p0) {
cid = p0;
setState(() {});
currentIndex = episodes.indexWhere((e) => e.cid == cid);
});
} }
void changeFucCall(item, i) async { void changeFucCall(item, i) async {

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
class SeasonPanel extends StatefulWidget { class SeasonPanel extends StatefulWidget {
@ -23,11 +24,16 @@ class SeasonPanel extends StatefulWidget {
class _SeasonPanelState extends State<SeasonPanel> { class _SeasonPanelState extends State<SeasonPanel> {
late List<EpisodeItem> episodes; late List<EpisodeItem> episodes;
late int cid;
late int currentIndex; late int currentIndex;
String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
cid = widget.cid!;
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
/// 根据 cid 找到对应集,找到对应 episodes /// 根据 cid 找到对应集,找到对应 episodes
/// 有多个episodes时只显示其中一个 /// 有多个episodes时只显示其中一个
@ -36,7 +42,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < sections.length; i++) {
List<EpisodeItem> episodesList = sections[i].episodes!; List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) { for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == widget.cid) { if (episodesList[j].cid == cid) {
episodes = episodesList; episodes = episodesList;
continue; continue;
} }
@ -47,7 +53,12 @@ class _SeasonPanelState extends State<SeasonPanel> {
// episodes = widget.ugcSeason.sections! // episodes = widget.ugcSeason.sections!
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id) // .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
// .episodes!; // .episodes!;
currentIndex = episodes.indexWhere((e) => e.cid == widget.cid); currentIndex = episodes.indexWhere((e) => e.cid == cid);
_videoDetailController.cid.listen((p0) {
cid = p0;
setState(() {});
currentIndex = episodes.indexWhere((e) => e.cid == cid);
});
} }
void changeFucCall(item, i) async { void changeFucCall(item, i) async {
@ -57,6 +68,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
item.aid, item.aid,
); );
currentIndex = i; currentIndex = i;
setState(() {});
Get.back(); Get.back();
} }

View File

@ -20,6 +20,7 @@ import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/introduction/index.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart';
import 'package:pilipala/pages/video/detail/related/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/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'widgets/app_bar.dart'; import 'widgets/app_bar.dart';
@ -41,6 +42,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
final ScrollController _extendNestCtr = ScrollController(); final ScrollController _extendNestCtr = ScrollController();
late StreamController<double> appbarStream; late StreamController<double> appbarStream;
late VideoIntroController videoIntroController; late VideoIntroController videoIntroController;
late BangumiIntroController bangumiIntroController;
late String heroTag; late String heroTag;
PlayerStatus playerStatus = PlayerStatus.playing; PlayerStatus playerStatus = PlayerStatus.playing;
@ -61,6 +63,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
heroTag = Get.arguments['heroTag']; heroTag = Get.arguments['heroTag'];
videoDetailController = Get.put(VideoDetailController(), tag: heroTag); videoDetailController = Get.put(VideoDetailController(), tag: heroTag);
videoIntroController = Get.put(VideoIntroController(), tag: heroTag); videoIntroController = Get.put(VideoIntroController(), tag: heroTag);
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
statusBarHeight = localCache.get('statusBarHeight'); statusBarHeight = localCache.get('statusBarHeight');
autoExitFullcreen = autoExitFullcreen =
setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); setting.get(SettingBoxKey.enableAutoExit, defaultValue: false);
@ -98,6 +101,23 @@ class _VideoDetailPageState extends State<VideoDetailPage>
if (autoExitFullcreen) { if (autoExitFullcreen) {
plPlayerController!.triggerFullScreen(status: false); 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();
}
// 播放完展示控制栏 // 播放完展示控制栏
try { try {
PiPStatus currentStatus = PiPStatus currentStatus =
@ -385,8 +405,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
const VideoIntroPanel(), const VideoIntroPanel(),
] else if (videoDetailController.videoType == ] else if (videoDetailController.videoType ==
SearchType.media_bangumi) ...[ SearchType.media_bangumi) ...[
BangumiIntroPanel( Obx(() => BangumiIntroPanel(
cid: videoDetailController.cid) cid: videoDetailController.cid.value)),
], ],
// if (videoDetailController.videoType == // if (videoDetailController.videoType ==
// SearchType.video) ...[ // SearchType.video) ...[

View File

@ -13,6 +13,7 @@ import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
class HeaderControl extends StatefulWidget implements PreferredSizeWidget { class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
@ -56,7 +57,7 @@ class _HeaderControlState extends State<HeaderControl> {
builder: (_) { builder: (_) {
return Container( return Container(
width: double.infinity, width: double.infinity,
height: 400, height: 440,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
@ -149,13 +150,14 @@ class _HeaderControlState extends State<HeaderControl> {
'当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}',
style: subTitleStyle), style: subTitleStyle),
), ),
// ListTile( ListTile(
// onTap: () {}, onTap: () => {Get.back(), showSetRepeat()},
// dense: true, dense: true,
// enabled: false, leading: const Icon(Icons.repeat, size: 20),
// leading: const Icon(Icons.play_circle_outline, size: 20), title: Text('播放顺序', style: titleStyle),
// title: Text('播放设置', style: titleStyle), subtitle: Text(widget.controller!.playRepeat.description,
// ), style: subTitleStyle),
),
ListTile( ListTile(
onTap: () => {Get.back(), showSetDanmaku()}, onTap: () => {Get.back(), showSetDanmaku()},
dense: true, dense: true,
@ -704,6 +706,60 @@ class _HeaderControlState extends State<HeaderControl> {
); );
} }
/// 播放顺序
void showSetRepeat() async {
showModalBottomSheet(
context: context,
elevation: 0,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return Container(
width: double.infinity,
height: 250,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
margin: const EdgeInsets.all(12),
child: Column(
children: [
SizedBox(
height: 45,
child: Center(child: Text('选择播放顺序', style: titleStyle))),
Expanded(
child: Material(
child: ListView(
children: [
for (var i in PlayRepeat.values) ...[
ListTile(
onTap: () {
widget.controller!.setPlayRepeat(i);
Get.back();
},
dense: true,
contentPadding:
const EdgeInsets.only(left: 20, right: 20),
title: Text(i.description),
trailing: widget.controller!.playRepeat == i
? Icon(
Icons.done,
color: Theme.of(context).colorScheme.primary,
)
: const SizedBox(),
)
],
],
),
),
),
],
),
);
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _ = widget.controller!; final _ = widget.controller!;

View File

@ -13,6 +13,7 @@ import 'package:media_kit_video/media_kit_video.dart';
import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/video.dart'; import 'package:pilipala/http/video.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:screen_brightness/screen_brightness.dart'; import 'package:screen_brightness/screen_brightness.dart';
@ -209,6 +210,9 @@ class PlPlayerController {
late double fontSizeVal; late double fontSizeVal;
late double danmakuSpeedVal; late double danmakuSpeedVal;
// 播放顺序相关
PlayRepeat playRepeat = PlayRepeat.pause;
// 添加一个私有构造函数 // 添加一个私有构造函数
PlPlayerController._() { PlPlayerController._() {
_videoType = videoType; _videoType = videoType;
@ -226,6 +230,12 @@ class PlPlayerController {
// 弹幕速度 // 弹幕速度
danmakuSpeedVal = danmakuSpeedVal =
localCache.get(LocalCacheKey.danmakuSpeed, defaultValue: 4.0); localCache.get(LocalCacheKey.danmakuSpeed, defaultValue: 4.0);
playRepeat = PlayRepeat.values.toList().firstWhere(
(e) =>
e.value ==
videoStorage.get(VideoBoxKey.playRepeat,
defaultValue: PlayRepeat.pause.value),
);
// _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { // _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) {
// if (status == PlayerStatus.playing) { // if (status == PlayerStatus.playing) {
// WakelockPlus.enable(); // WakelockPlus.enable();
@ -910,6 +920,11 @@ class PlPlayerController {
} }
} }
setPlayRepeat(PlayRepeat type) {
playRepeat = type;
videoStorage.put(VideoBoxKey.playRepeat, type.value);
}
Future<void> dispose({String type = 'single'}) async { Future<void> dispose({String type = 'single'}) async {
// 每次减1最后销毁 // 每次减1最后销毁
if (type == 'single' && playerCount.value > 1) { if (type == 'single' && playerCount.value > 1) {

View File

@ -0,0 +1,25 @@
enum PlayRepeat {
pause,
listOrder,
singleCycle,
listCycle,
}
extension PlayRepeatExtension on PlayRepeat {
static final List<String> _descList = [
'播完暂停',
'顺序播放',
'单个循环',
'列表循环',
];
get description => _descList[index];
static final List<double> _valueList = [
1,
2,
3,
4,
];
get value => _valueList[index];
get defaultValue => _valueList[1];
}

View File

@ -157,4 +157,6 @@ class VideoBoxKey {
static const String videoBrightness = 'videoBrightness'; static const String videoBrightness = 'videoBrightness';
// 倍速 // 倍速
static const String videoSpeed = 'videoSpeed'; static const String videoSpeed = 'videoSpeed';
// 播放顺序
static const String playRepeat = 'playRepeat';
} }