mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: medialist: reverse play #70
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -28,3 +28,22 @@ Widget iconButton({
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget mediumButton({
|
||||||
|
String? tooltip,
|
||||||
|
IconData? icon,
|
||||||
|
VoidCallback? onPressed,
|
||||||
|
}) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: tooltip,
|
||||||
|
icon: Icon(icon),
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: onPressed,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:PiliPalaX/common/constants.dart';
|
import 'package:PiliPalaX/common/constants.dart';
|
||||||
|
import 'package:PiliPalaX/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||||
import 'package:PiliPalaX/http/video.dart';
|
import 'package:PiliPalaX/http/video.dart';
|
||||||
import 'package:PiliPalaX/models/bangumi/info.dart' as bangumi;
|
import 'package:PiliPalaX/models/bangumi/info.dart' as bangumi;
|
||||||
@@ -291,7 +292,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: _favStream?.stream,
|
stream: _favStream?.stream,
|
||||||
builder: (context, snapshot) => snapshot.hasData
|
builder: (context, snapshot) => snapshot.hasData
|
||||||
? _mediumButton(
|
? mediumButton(
|
||||||
tooltip: _seasonFav == 1 ? '取消订阅' : '订阅',
|
tooltip: _seasonFav == 1 ? '取消订阅' : '订阅',
|
||||||
icon: _seasonFav == 1
|
icon: _seasonFav == 1
|
||||||
? Icons.notifications_off_outlined
|
? Icons.notifications_off_outlined
|
||||||
@@ -313,7 +314,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
_mediumButton(
|
mediumButton(
|
||||||
tooltip: '跳至顶部',
|
tooltip: '跳至顶部',
|
||||||
icon: Icons.vertical_align_top,
|
icon: Icons.vertical_align_top,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -331,7 +332,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_mediumButton(
|
mediumButton(
|
||||||
tooltip: '跳至底部',
|
tooltip: '跳至底部',
|
||||||
icon: Icons.vertical_align_bottom,
|
icon: Icons.vertical_align_bottom,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -349,7 +350,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_mediumButton(
|
mediumButton(
|
||||||
tooltip: '跳至当前',
|
tooltip: '跳至当前',
|
||||||
icon: Icons.my_location,
|
icon: Icons.my_location,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -382,7 +383,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: _indexStream?.stream,
|
stream: _indexStream?.stream,
|
||||||
initialData: _index,
|
initialData: _index,
|
||||||
builder: (context, snapshot) => _mediumButton(
|
builder: (context, snapshot) => mediumButton(
|
||||||
tooltip: reverse[snapshot.data] ? '顺序' : '倒序',
|
tooltip: reverse[snapshot.data] ? '顺序' : '倒序',
|
||||||
icon: !reverse[snapshot.data]
|
icon: !reverse[snapshot.data]
|
||||||
? MdiIcons.sortNumericAscending
|
? MdiIcons.sortNumericAscending
|
||||||
@@ -395,7 +396,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.onClose != null)
|
if (widget.onClose != null)
|
||||||
_mediumButton(
|
mediumButton(
|
||||||
tooltip: '关闭',
|
tooltip: '关闭',
|
||||||
icon: Icons.close,
|
icon: Icons.close,
|
||||||
onPressed: widget.onClose,
|
onPressed: widget.onClose,
|
||||||
@@ -437,7 +438,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget get _reverseButton => _mediumButton(
|
Widget get _reverseButton => mediumButton(
|
||||||
tooltip: widget.isReversed == true ? '正序播放' : '倒序播放',
|
tooltip: widget.isReversed == true ? '正序播放' : '倒序播放',
|
||||||
icon: widget.isReversed == true
|
icon: widget.isReversed == true
|
||||||
? MdiIcons.sortDescending
|
? MdiIcons.sortDescending
|
||||||
@@ -461,25 +462,6 @@ class _ListSheetContentState extends State<ListSheetContent>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _mediumButton({
|
|
||||||
String? tooltip,
|
|
||||||
IconData? icon,
|
|
||||||
VoidCallback? onPressed,
|
|
||||||
}) {
|
|
||||||
return SizedBox(
|
|
||||||
width: 34,
|
|
||||||
height: 34,
|
|
||||||
child: IconButton(
|
|
||||||
tooltip: tooltip,
|
|
||||||
icon: Icon(icon),
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: onPressed,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBody(i, episodes) => Material(
|
Widget _buildBody(i, episodes) => Material(
|
||||||
child: ScrollablePositionedList.separated(
|
child: ScrollablePositionedList.separated(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ class MediaVideoItemModel {
|
|||||||
this.forbidFav,
|
this.forbidFav,
|
||||||
this.moreType,
|
this.moreType,
|
||||||
this.businessOid,
|
this.businessOid,
|
||||||
this.isReversed = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
int? id;
|
int? id;
|
||||||
@@ -65,7 +64,6 @@ class MediaVideoItemModel {
|
|||||||
bool? forbidFav;
|
bool? forbidFav;
|
||||||
int? moreType;
|
int? moreType;
|
||||||
int? businessOid;
|
int? businessOid;
|
||||||
bool isReversed;
|
|
||||||
|
|
||||||
factory MediaVideoItemModel.fromJson(Map<String, dynamic> json) =>
|
factory MediaVideoItemModel.fromJson(Map<String, dynamic> json) =>
|
||||||
MediaVideoItemModel(
|
MediaVideoItemModel(
|
||||||
@@ -103,7 +101,6 @@ class MediaVideoItemModel {
|
|||||||
forbidFav: json["forbid_fav"],
|
forbidFav: json["forbid_fav"],
|
||||||
moreType: json["more_type"],
|
moreType: json["more_type"],
|
||||||
businessOid: json["business_oid"],
|
businessOid: json["business_oid"],
|
||||||
isReversed: false,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ class FavDetailController extends MultiSelectController {
|
|||||||
'oid': element.id,
|
'oid': element.id,
|
||||||
'favTitle': item.value.title,
|
'favTitle': item.value.title,
|
||||||
'count': item.value.mediaCount,
|
'count': item.value.mediaCount,
|
||||||
|
'desc': true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ class LaterController extends MultiSelectController {
|
|||||||
'count': list.length,
|
'count': list.length,
|
||||||
'favTitle': '稍后再看',
|
'favTitle': '稍后再看',
|
||||||
'mediaId': GStorage.userInfo.get('userInfoCache')?.mid,
|
'mediaId': GStorage.userInfo.get('userInfoCache')?.mid,
|
||||||
|
'desc': false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:PiliPalaX/models/space_archive/item.dart';
|
|||||||
import 'package:PiliPalaX/pages/common/common_controller.dart';
|
import 'package:PiliPalaX/pages/common/common_controller.dart';
|
||||||
import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart'
|
import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart'
|
||||||
show ContributeType;
|
show ContributeType;
|
||||||
|
import 'package:PiliPalaX/utils/extension.dart';
|
||||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@@ -103,6 +104,13 @@ class MemberVideoCtr extends CommonController {
|
|||||||
SmartDialog.showToast('已跳过不支持播放的视频');
|
SmartDialog.showToast('已跳过不支持播放的视频');
|
||||||
}
|
}
|
||||||
final String heroTag = Utils.makeHeroTag(element.bvid);
|
final String heroTag = Utils.makeHeroTag(element.bvid);
|
||||||
|
bool desc = seasonId != null ? false : true;
|
||||||
|
desc = (seasonId != null || seriesId != null) &&
|
||||||
|
(type == ContributeType.video
|
||||||
|
? order.value == 'click'
|
||||||
|
: sort.value == 'asc')
|
||||||
|
? desc.not
|
||||||
|
: desc;
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/video?bvid=${element.bvid}&cid=${element.firstCid}',
|
'/video?bvid=${element.bvid}&cid=${element.firstCid}',
|
||||||
arguments: {
|
arguments: {
|
||||||
@@ -118,9 +126,11 @@ class MemberVideoCtr extends CommonController {
|
|||||||
'mediaType': RegExp(r'page_type=([\d]+)')
|
'mediaType': RegExp(r'page_type=([\d]+)')
|
||||||
.firstMatch('${episodicButton?.uri}')
|
.firstMatch('${episodicButton?.uri}')
|
||||||
?.group(1),
|
?.group(1),
|
||||||
'reverse': type == ContributeType.video
|
'desc': desc,
|
||||||
? order.value == 'click'
|
'sortField':
|
||||||
: sort.value == 'asc',
|
type == ContributeType.video && order.value == 'click'
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
// 页面来源 稍后再看 收藏夹
|
// 页面来源 稍后再看 收藏夹
|
||||||
String sourceType = 'normal';
|
String sourceType = 'normal';
|
||||||
|
late bool _mediaDesc = false;
|
||||||
late RxList<MediaVideoItemModel> mediaList = <MediaVideoItemModel>[].obs;
|
late RxList<MediaVideoItemModel> mediaList = <MediaVideoItemModel>[].obs;
|
||||||
late String watchLaterTitle = '';
|
late String watchLaterTitle = '';
|
||||||
bool get isPlayAll => ['watchLater', 'fav', 'archive'].contains(sourceType);
|
bool get isPlayAll => ['watchLater', 'fav', 'archive'].contains(sourceType);
|
||||||
@@ -279,6 +280,7 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
if (sourceType != 'normal') {
|
if (sourceType != 'normal') {
|
||||||
watchLaterTitle = Get.arguments['favTitle'];
|
watchLaterTitle = Get.arguments['favTitle'];
|
||||||
|
_mediaDesc = Get.arguments['desc'];
|
||||||
getMediaList();
|
getMediaList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,31 +329,44 @@ class VideoDetailController extends GetxController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMediaList() async {
|
void getMediaList([bool isReverse = false]) async {
|
||||||
if (Get.arguments['count'] != null &&
|
if (isReverse.not &&
|
||||||
|
Get.arguments['count'] != null &&
|
||||||
mediaList.length >= Get.arguments['count']) {
|
mediaList.length >= Get.arguments['count']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool desc =
|
|
||||||
_mediaType == 2 || Get.arguments['mediaType'] == '8' ? false : true;
|
|
||||||
var res = await UserHttp.getMediaList(
|
var res = await UserHttp.getMediaList(
|
||||||
type: Get.arguments['mediaType'] ?? _mediaType,
|
type: Get.arguments['mediaType'] ?? _mediaType,
|
||||||
bizId: Get.arguments['mediaId'] ?? -1,
|
bizId: Get.arguments['mediaId'] ?? -1,
|
||||||
ps: 20,
|
ps: 20,
|
||||||
oid: mediaList.isEmpty ? null : mediaList.last.id,
|
oid: isReverse || mediaList.isEmpty ? null : mediaList.last.id,
|
||||||
otype: mediaList.isEmpty ? null : mediaList.last.type,
|
otype: isReverse || mediaList.isEmpty ? null : mediaList.last.type,
|
||||||
desc:
|
desc: _mediaDesc,
|
||||||
Get.arguments['mediaType'] != null && Get.arguments['reverse'] == true
|
sortField: Get.arguments['sortField'] ?? 1,
|
||||||
? desc.not
|
|
||||||
: desc,
|
|
||||||
sortField:
|
|
||||||
Get.arguments['mediaType'] == null && Get.arguments['reverse'] == true
|
|
||||||
? 2
|
|
||||||
: 1,
|
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
if (res['data'].isNotEmpty) {
|
if (res['data'].isNotEmpty) {
|
||||||
mediaList.addAll(res['data']);
|
if (isReverse) {
|
||||||
|
mediaList.value = res['data'];
|
||||||
|
try {
|
||||||
|
for (MediaVideoItemModel item in mediaList) {
|
||||||
|
if (item.cid == null) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Get.find<VideoIntroController>(tag: heroTag)
|
||||||
|
.changeSeasonOrbangu(
|
||||||
|
null,
|
||||||
|
mediaList.first.bvid,
|
||||||
|
mediaList.first.cid,
|
||||||
|
mediaList.first.aid,
|
||||||
|
mediaList.first.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
} else {
|
||||||
|
mediaList.addAll(res['data']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
@@ -366,9 +381,14 @@ class VideoDetailController extends GetxController
|
|||||||
mediaList: mediaList,
|
mediaList: mediaList,
|
||||||
changeMediaList: changeMediaList,
|
changeMediaList: changeMediaList,
|
||||||
panelTitle: watchLaterTitle,
|
panelTitle: watchLaterTitle,
|
||||||
bvid: bvid,
|
getBvId: () => bvid,
|
||||||
count: Get.arguments['count'],
|
count: Get.arguments['count'],
|
||||||
loadMoreMedia: getMediaList,
|
loadMoreMedia: getMediaList,
|
||||||
|
desc: _mediaDesc,
|
||||||
|
onReverse: () {
|
||||||
|
_mediaDesc = !_mediaDesc;
|
||||||
|
getMediaList(true);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/stat/danmu.dart';
|
import 'package:PiliPalaX/common/widgets/stat/danmu.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/stat/view.dart';
|
import 'package:PiliPalaX/common/widgets/stat/view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -9,6 +10,7 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
|||||||
import 'package:PiliPalaX/http/search.dart';
|
import 'package:PiliPalaX/http/search.dart';
|
||||||
import 'package:PiliPalaX/models/video/later.dart';
|
import 'package:PiliPalaX/models/video/later.dart';
|
||||||
import 'package:PiliPalaX/utils/utils.dart';
|
import 'package:PiliPalaX/utils/utils.dart';
|
||||||
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
class MediaListPanel extends StatefulWidget {
|
class MediaListPanel extends StatefulWidget {
|
||||||
@@ -17,17 +19,21 @@ class MediaListPanel extends StatefulWidget {
|
|||||||
required this.mediaList,
|
required this.mediaList,
|
||||||
this.changeMediaList,
|
this.changeMediaList,
|
||||||
this.panelTitle,
|
this.panelTitle,
|
||||||
this.bvid,
|
required this.getBvId,
|
||||||
required this.loadMoreMedia,
|
required this.loadMoreMedia,
|
||||||
required this.count,
|
required this.count,
|
||||||
|
required this.desc,
|
||||||
|
required this.onReverse,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<MediaVideoItemModel> mediaList;
|
final List<MediaVideoItemModel> mediaList;
|
||||||
final Function? changeMediaList;
|
final Function? changeMediaList;
|
||||||
final String? panelTitle;
|
final String? panelTitle;
|
||||||
final String? bvid;
|
final Function getBvId;
|
||||||
final VoidCallback loadMoreMedia;
|
final VoidCallback loadMoreMedia;
|
||||||
final int? count;
|
final int? count;
|
||||||
|
final bool? desc;
|
||||||
|
final VoidCallback onReverse;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MediaListPanel> createState() => _MediaListPanelState();
|
State<MediaListPanel> createState() => _MediaListPanelState();
|
||||||
@@ -41,7 +47,7 @@ class _MediaListPanelState extends State<MediaListPanel> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
int index =
|
int index =
|
||||||
widget.mediaList.indexWhere((item) => item.bvid == widget.bvid);
|
widget.mediaList.indexWhere((item) => item.bvid == widget.getBvId());
|
||||||
if (index != -1 && index != 0) {
|
if (index != -1 && index != 0) {
|
||||||
try {
|
try {
|
||||||
_scrollController.jumpTo(index: index);
|
_scrollController.jumpTo(index: index);
|
||||||
@@ -64,8 +70,17 @@ class _MediaListPanelState extends State<MediaListPanel> {
|
|||||||
titleSpacing: 16,
|
titleSpacing: 16,
|
||||||
title: Text(widget.panelTitle ?? '稍后再看'),
|
title: Text(widget.panelTitle ?? '稍后再看'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
if (widget.desc != null)
|
||||||
icon: const Icon(Icons.close, size: 20),
|
mediumButton(
|
||||||
|
tooltip: widget.desc == true ? '顺序播放' : '倒序播放',
|
||||||
|
icon: widget.desc == true
|
||||||
|
? MdiIcons.sortAscending
|
||||||
|
: MdiIcons.sortDescending,
|
||||||
|
onPressed: widget.onReverse,
|
||||||
|
),
|
||||||
|
mediumButton(
|
||||||
|
tooltip: '关闭',
|
||||||
|
icon: Icons.close,
|
||||||
onPressed: Get.back,
|
onPressed: Get.back,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 14),
|
||||||
@@ -160,14 +175,15 @@ class _MediaListPanelState extends State<MediaListPanel> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight:
|
fontWeight:
|
||||||
item.bvid == widget.bvid
|
item.bvid == widget.getBvId()
|
||||||
? FontWeight.bold
|
? FontWeight.bold
|
||||||
: null,
|
: null,
|
||||||
color: item.bvid == widget.bvid
|
color:
|
||||||
? Theme.of(context)
|
item.bvid == widget.getBvId()
|
||||||
.colorScheme
|
? Theme.of(context)
|
||||||
.primary
|
.colorScheme
|
||||||
: null,
|
.primary
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|||||||
Reference in New Issue
Block a user