refa: vertical video page (#328)

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
dom
2025-02-26 17:21:24 +08:00
committed by GitHub
parent 41f251ad50
commit 8434c488da
30 changed files with 2534 additions and 224 deletions

View File

@@ -29,45 +29,6 @@ class BangumiCardV extends StatelessWidget {
onTap: () async {
final int seasonId = bangumiItem.seasonId;
Utils.viewBangumi(seasonId: seasonId);
// SmartDialog.showLoading(msg: '获取中...');
// final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
// SmartDialog.dismiss().then((value) {
// if (res['status']) {
// if (res['data'].episodes.isEmpty) {
// SmartDialog.showToast('资源加载失败');
// return;
// }
// EpisodeItem episode = res['data'].episodes.first;
// int? epId = res['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in res['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// // debugPrint('epId');
// // debugPrint(epId);
// String heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// 'bangumiItem': res['data'],
// },
// );
// } else {
// SmartDialog.showToast(res['msg']);
// }
// });
},
child: Column(
children: [

View File

@@ -24,45 +24,6 @@ class BangumiCardVMemberHome extends StatelessWidget {
onTap: () async {
final int seasonId = int.tryParse(bangumiItem.param ?? '') ?? -1;
Utils.viewBangumi(seasonId: seasonId);
// SmartDialog.showLoading(msg: '获取中...');
// final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
// SmartDialog.dismiss().then((value) {
// if (res['status']) {
// if (res['data'].episodes.isEmpty) {
// SmartDialog.showToast('资源加载失败');
// return;
// }
// EpisodeItem episode = res['data'].episodes.first;
// int? epId = res['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in res['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// // debugPrint('epId');
// // debugPrint(epId);
// String heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// 'bangumiItem': res['data'],
// },
// );
// } else {
// SmartDialog.showToast(res['msg']);
// }
// });
},
onLongPress: () => imageSaveDialog(
context: context,

View File

@@ -1,7 +1,6 @@
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/http/search.dart';
@@ -35,8 +34,8 @@ Widget addWidget(item, context, type, {floor = 1}) {
String cover = dynamicProperty[type].cover;
try {
int cid = await SearchHttp.ab2c(bvid: bvid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: {
'pic': cover,
'heroTag': Utils.makeHeroTag(bvid),

View File

@@ -238,8 +238,8 @@ InlineSpan? richNode(item, context) {
onTap: () async {
try {
int cid = await SearchHttp.ab2c(bvid: i.rid);
Get.toNamed(
'/video?bvid=${i.rid}&cid=$cid',
Utils.toViewPage(
'bvid=${i.rid}&cid=$cid',
arguments: {
'pic': null,
'heroTag': Utils.makeHeroTag(i.rid),

View File

@@ -145,8 +145,8 @@ class FavDetailController extends MultiSelectController {
if (element.bvid != list.first.bvid) {
SmartDialog.showToast('已跳过不支持播放的视频');
}
Get.toNamed(
'/video?bvid=${element.bvid}&cid=${element.cid}',
Utils.toViewPage(
'bvid=${element.bvid}&cid=${element.cid}',
arguments: {
'videoItem': element,
'heroTag': Utils.makeHeroTag(element.bvid),

View File

@@ -67,8 +67,8 @@ class FavVideoCardH extends StatelessWidget {
// if (seasonId != null) {
// parameters['seasonId'] = seasonId.toString();
// }
Get.toNamed(
'/video',
Utils.toViewPage(
'',
parameters: parameters,
arguments: {
'videoItem': videoItem,

View File

@@ -89,8 +89,8 @@ class HistoryItem extends StatelessWidget {
} else {
int cid = videoItem.history.cid ??
await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: {
'heroTag': Utils.makeHeroTag(cid),
'pic': videoItem.cover,
@@ -103,49 +103,14 @@ class HistoryItem extends StatelessWidget {
} else {
if (videoItem.history.epid != '') {
Utils.viewBangumi(epId: videoItem.history.epid);
// SmartDialog.showLoading(msg: '获取中...');
// var res =
// await SearchHttp.bangumiInfo(epId: videoItem.history.epid);
// SmartDialog.dismiss();
// if (res['status']) {
// var bangumiDetail = res['data'];
// EpisodeItem episode = res['data'].episodes.first;
// int? epId = res['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in res['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// String seasonId = bangumiDetail.seasonId;
// dynamic heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// 'bangumiItem': res['data'],
// },
// );
// } else {
// SmartDialog.showToast(res['msg']);
// }
}
}
} else {
int cid = videoItem.history.cid ??
// videoItem.history.oid ??
await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: {
'heroTag': Utils.makeHeroTag(aid),
'pic': videoItem.cover,

View File

@@ -164,8 +164,8 @@ class LaterController extends MultiSelectController {
if (item.bvid != list.first.bvid) {
SmartDialog.showToast('已跳过不支持播放的视频');
}
Get.toNamed(
'/video?bvid=${item.bvid}&cid=${item.cid}',
Utils.toViewPage(
'bvid=${item.bvid}&cid=${item.cid}',
arguments: {
'videoItem': item,
'heroTag': Utils.makeHeroTag(item.bvid),

View File

@@ -104,8 +104,8 @@ class MemberVideoCtr extends CommonController {
?.group(1);
dynamic bvid = IdUtils.av2bv(int.tryParse(oid) ?? 0);
dynamic cid = await SearchHttp.ab2c(aid: oid, bvid: bvid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: {
'heroTag': Utils.makeHeroTag(oid),
'sourceType': 'archive',
@@ -145,8 +145,8 @@ class MemberVideoCtr extends CommonController {
: sort.value == 'asc')
? desc.not
: desc;
Get.toNamed(
'/video?bvid=${element.bvid}&cid=${element.firstCid}',
Utils.toViewPage(
'bvid=${element.bvid}&cid=${element.firstCid}',
arguments: {
'videoItem': element,
'heroTag': Utils.makeHeroTag(element.bvid),

View File

@@ -1,6 +1,5 @@
import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
@@ -26,8 +25,8 @@ class MemberCoinsItem extends StatelessWidget {
onTap: () async {
int cid =
await SearchHttp.ab2c(aid: coinItem.aid, bvid: coinItem.bvid);
Get.toNamed(
'/video?bvid=${coinItem.bvid}&cid=$cid',
Utils.toViewPage(
'bvid=${coinItem.bvid}&cid=$cid',
arguments: {
'videoItem': coinItem,
'heroTag': Utils.makeHeroTag(coinItem.aid)

View File

@@ -256,8 +256,8 @@ class SearchDynamic extends StatelessWidget {
return ListTile(
dense: true,
onTap: () {
Get.toNamed(
'/video?bvid=${IdUtils.av2bv(json['aid'])}&cid=${json['cid']}',
Utils.toViewPage(
'bvid=${IdUtils.av2bv(json['aid'])}&cid=${json['cid']}',
arguments: {
'heroTag': Utils.makeHeroTag(json['aid']),
},

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
@@ -25,8 +24,8 @@ class MemberSeasonsItem extends StatelessWidget {
onTap: () async {
int cid =
await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid);
Get.toNamed(
'/video?bvid=${seasonItem.bvid}&cid=$cid',
Utils.toViewPage(
'bvid=${seasonItem.bvid}&cid=$cid',
arguments: {
'videoItem': seasonItem,
'heroTag': Utils.makeHeroTag(seasonItem.aid)

View File

@@ -2101,6 +2101,16 @@ List<SettingsModel> get extraSettings => [
setKey: SettingBoxKey.antiGoodsReply,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '使用可折叠的播放页面',
leading: Icon(Icons.video_settings),
setKey: SettingBoxKey.collapsibleVideoPage,
defaultVal: false,
onChanged: (value) {
GStorage.collapsibleVideoPage = value;
},
),
SettingsModel(
settingsType: SettingsType.sw1tch,
enableFeedback: true,

View File

@@ -1,5 +1,4 @@
import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/stat/danmu.dart';
@@ -34,8 +33,8 @@ class SubVideoCardH extends StatelessWidget {
'cid': cid.toString(),
};
Get.toNamed(
'/video',
Utils.toViewPage(
'',
parameters: parameters,
arguments: {
'videoItem': videoItem,

View File

@@ -27,6 +27,7 @@ import 'package:PiliPlus/utils/extension.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -126,6 +127,9 @@ class VideoDetailController extends GetxController
PlayerStatus? playerStatus;
StreamSubscription<Duration>? positionSubscription;
late final scrollKey = GlobalKey<ExtendedNestedScrollViewState>();
late final RxString direction = 'horizontal'.obs;
bool imageStatus = false;
void onViewImage() {
@@ -1082,6 +1086,11 @@ class VideoDetailController extends GetxController
baseUrl: videoUrl,
codecs: 'avc1',
quality: VideoQualityCode.fromCode(data.quality!)!);
direction.value = firstVideo.width != null && firstVideo.height != null
? firstVideo.width! > firstVideo.height!
? 'horizontal'
: 'vertical'
: 'horizontal';
currentDecodeFormats = VideoDecodeFormatsCode.fromString('avc1')!;
currentVideoQa = VideoQualityCode.fromCode(data.quality!)!;
if (autoPlay.value) {
@@ -1152,6 +1161,11 @@ class VideoDetailController extends GetxController
firstVideo = videosList.firstWhere(
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
orElse: () => videosList.first);
direction.value = firstVideo.width != null && firstVideo.height != null
? firstVideo.width! > firstVideo.height!
? 'horizontal'
: 'vertical'
: 'horizontal';
// videoUrl = enableCDN
// ? VideoUtils.getCdnUrl(firstVideo)

View File

@@ -821,13 +821,6 @@ class VideoIntroController extends GetxController
(relatedCtr.loadingState.value as Success).response[0];
try {
if (videoItem.cid != null) {
// Get.offNamed(
// '/video?bvid=${videoItem.bvid}&cid=${videoItem.cid}',
// arguments: {
// 'videoItem': videoItem,
// 'heroTag': heroTag,
// },
// );
changeSeasonOrbangu(
null,
videoItem.bvid,
@@ -837,12 +830,14 @@ class VideoIntroController extends GetxController
);
} else {
SearchHttp.ab2c(aid: videoItem.aid, bvid: videoItem.bvid).then(
(cid) => Get.offNamed(
'/video?bvid=${videoItem.bvid}&cid=${videoItem.cid}',
arguments: {
'videoItem': videoItem,
'heroTag': heroTag,
}),
(cid) => Utils.toViewPage(
'bvid=${videoItem.bvid}&cid=${videoItem.cid}',
arguments: {
'videoItem': videoItem,
'heroTag': heroTag,
},
off: true,
),
);
}
} catch (err) {

View File

@@ -305,8 +305,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
final ThemeData t = Theme.of(context);
return SliverLayoutBuilder(
builder: (BuildContext context, SliverConstraints constraints) {
bool isHorizontal = constraints.crossAxisExtent >
constraints.viewportMainAxisExtent * 1.25;
// bool isHorizontal = constraints.crossAxisExtent >
// constraints.viewportMainAxisExtent * 1.25;
bool isHorizontal = context.orientation == Orientation.landscape;
return SliverPadding(
padding: const EdgeInsets.only(
left: StyleString.safeSpace,

View File

@@ -6,8 +6,11 @@ import 'package:PiliPlus/http/reply.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/id_utils.dart';
import 'package:fixnum/fixnum.dart' as $fixnum;
import 'package:flutter/material.dart';
import 'package:get/get_state_manager/src/rx_flutter/rx_ticket_provider_mixin.dart';
class VideoReplyController extends ReplyController {
class VideoReplyController extends ReplyController
with GetTickerProviderStateMixin {
VideoReplyController({required this.aid});
// 视频aid 请求时使用的oid
int aid;
@@ -15,6 +18,25 @@ class VideoReplyController extends ReplyController {
@override
dynamic get sourceId => IdUtils.av2bv(aid);
bool _isFabVisible = true;
late final AnimationController fabAnimationCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 100))
..forward();
void showFab() {
if (!_isFabVisible) {
_isFabVisible = true;
fabAnimationCtr.forward();
}
}
void hideFab() {
if (_isFabVisible) {
_isFabVisible = false;
fabAnimationCtr.reverse();
}
}
@override
Future<LoadingState> customGetData() => GlobalData().grpcReply
? ReplyHttp.replyListGrpc(
@@ -34,4 +56,10 @@ class VideoReplyController extends ReplyController {
page: currentPage,
antiGoodsReply: antiGoodsReply,
);
@override
void onClose() {
fabAnimationCtr.dispose();
super.onClose();
}
}

View File

@@ -26,6 +26,7 @@ class VideoReplyPanel extends StatefulWidget {
final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed;
final Function(List<String>, int)? callback;
final bool? needController;
const VideoReplyPanel({
super.key,
@@ -38,6 +39,7 @@ class VideoReplyPanel extends StatefulWidget {
this.onViewImage,
this.onDismissed,
this.callback,
this.needController,
});
@override
@@ -47,9 +49,7 @@ class VideoReplyPanel extends StatefulWidget {
class _VideoReplyPanelState extends State<VideoReplyPanel>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
late VideoReplyController _videoReplyController;
late AnimationController fabAnimationCtr;
bool _isFabVisible = true;
String replyLevel = '1';
late String heroTag;
@@ -66,52 +66,33 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
replyLevel = widget.replyLevel ?? '1';
_videoReplyController = Get.find<VideoReplyController>(tag: heroTag);
fabAnimationCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 100));
fabAnimationCtr.forward();
scrollListener();
if (widget.needController != false) {
_videoReplyController.scrollController.addListener(listener);
}
}
@override
void dispose() {
_videoReplyController.scrollController.removeListener(listener);
fabAnimationCtr.dispose();
if (widget.needController != false) {
_videoReplyController.scrollController.removeListener(listener);
}
super.dispose();
}
void scrollListener() {
_videoReplyController.scrollController.addListener(listener);
}
void listener() {
final ScrollDirection direction =
_videoReplyController.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
if (mounted) {
_showFab();
_videoReplyController.showFab();
}
} else if (direction == ScrollDirection.reverse) {
if (mounted) {
_hideFab();
_videoReplyController.hideFab();
}
}
}
void _showFab() {
if (!_isFabVisible) {
_isFabVisible = true;
fabAnimationCtr.forward();
}
}
void _hideFab() {
if (_isFabVisible) {
_isFabVisible = false;
fabAnimationCtr.reverse();
}
}
@override
Widget build(BuildContext context) {
super.build(context);
@@ -122,8 +103,14 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
child: Stack(
children: [
CustomScrollView(
controller: _videoReplyController.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
controller: widget.needController == false
? null
: _videoReplyController.scrollController,
physics: widget.needController == false
? const NeverScrollableScrollPhysics(
parent: ClampingScrollPhysics(),
)
: const AlwaysScrollableScrollPhysics(),
// key: const PageStorageKey<String>('评论'),
slivers: <Widget>[
SliverPersistentHeader(
@@ -182,7 +169,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
begin: const Offset(0, 2),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
parent: _videoReplyController.fabAnimationCtr,
curve: Curves.easeInOut,
)),
child: FloatingActionButton(

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactivevie
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -182,8 +181,8 @@ class ChatItem extends StatelessWidget {
var bvid = content["bvid"];
final int cid = await SearchHttp.ab2c(bvid: bvid);
SmartDialog.dismiss<dynamic>().then(
(e) => Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid',
(e) => Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: <String, String?>{
'pic': content['thumb'],
'heroTag': Utils.makeHeroTag(bvid),
@@ -230,8 +229,8 @@ class ChatItem extends StatelessWidget {
var bvid = content["bvid"];
final int cid = await SearchHttp.ab2c(bvid: bvid);
SmartDialog.dismiss().then(
(_) => Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
(_) => Utils.toViewPage(
'bvid=$bvid&cid=$cid',
arguments: {
'pic': content['thumb'],
'heroTag': Utils.makeHeroTag(bvid),
@@ -317,8 +316,7 @@ class ChatItem extends StatelessWidget {
SmartDialog.showLoading();
final int cid = await SearchHttp.ab2c(bvid: bvid);
SmartDialog.dismiss<dynamic>().then(
(e) => Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid',
(e) => Utils.toViewPage('bvid=$bvid&cid=$cid',
arguments: <String, String?>{
'pic': i['cover_url'],
'heroTag': Utils.makeHeroTag(bvid),