diff --git a/lib/http/user.dart b/lib/http/user.dart index 2704ab89..a1aa733a 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -1,10 +1,7 @@ -import 'dart:convert'; - import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/models/video/later.dart'; import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:html/parser.dart'; import '../common/constants.dart'; import '../models/model_hot_video_item.dart'; import '../models/user/fav_detail.dart'; @@ -517,7 +514,9 @@ class UserHttp { required int bizId, required int ps, int? oid, - int otype = 2, + int? otype, + bool withCurrent = false, + bool desc = true, }) async { var res = await Request().get( Api.mediaList, @@ -525,14 +524,14 @@ class UserHttp { 'mobi_app': 'web', 'type': type, 'biz_id': bizId, - 'oid': oid ?? '', - 'otype': otype, // video:2 // bangumi: 24 + if (oid != null) 'oid': oid, + if (otype != null) 'otype': otype, // video:2 // bangumi: 24 'ps': ps, 'direction': false, - 'desc': true, + 'desc': desc, 'sort_field': 1, 'tid': 0, - 'with_current': false, + 'with_current': withCurrent, }, ); if (res.data['code'] == 0) { @@ -551,30 +550,30 @@ class UserHttp { } // 解析收藏夹视频 - static Future parseFavVideo({ - required int mediaId, - required int oid, - required String bvid, - }) async { - var res = await Request().get( - 'https://www.bilibili.com/list/ml$mediaId', - queryParameters: { - 'oid': mediaId, - 'bvid': bvid, - }, - ); - String scriptContent = - extractScriptContents(parse(res.data).body!.outerHtml)[0]; - int startIndex = scriptContent.indexOf('{'); - int endIndex = scriptContent.lastIndexOf('};'); - String jsonContent = scriptContent.substring(startIndex, endIndex + 1); - // 解析JSON字符串为Map - Map jsonData = json.decode(jsonContent); - return { - 'status': true, - 'data': jsonData['resourceList'] - .map((e) => MediaVideoItemModel.fromJson(e)) - .toList() - }; - } + // static Future parseFavVideo({ + // required int mediaId, + // required int oid, + // required String bvid, + // }) async { + // var res = await Request().get( + // 'https://www.bilibili.com/list/ml$mediaId', + // queryParameters: { + // 'oid': mediaId, + // 'bvid': bvid, + // }, + // ); + // String scriptContent = + // extractScriptContents(parse(res.data).body!.outerHtml)[0]; + // int startIndex = scriptContent.indexOf('{'); + // int endIndex = scriptContent.lastIndexOf('};'); + // String jsonContent = scriptContent.substring(startIndex, endIndex + 1); + // // 解析JSON字符串为Map + // Map jsonData = json.decode(jsonContent); + // return { + // 'status': true, + // 'data': jsonData['resourceList'] + // .map((e) => MediaVideoItemModel.fromJson(e)) + // .toList() + // }; + // } } diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 10adafd6..116d8add 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -125,7 +125,7 @@ class FavDetailController extends MultiSelectController { ); } - Future toViewPlayAll() async { + void toViewPlayAll() { if (loadingState.value is Success) { List list = (loadingState.value as Success).response; for (FavDetailItemData element in list) { diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 3306ddea..04e03a44 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -1,6 +1,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/models/model_hot_video_item.dart'; import 'package:PiliPalaX/pages/common/multi_select_controller.dart'; +import 'package:PiliPalaX/utils/storage.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -165,7 +166,7 @@ class LaterController extends MultiSelectController { } // 稍后再看播放全部 - Future toViewPlayAll() async { + void toViewPlayAll() { if (loadingState.value is Success) { List list = (loadingState.value as Success).response; for (HotVideoItemModel item in list) { @@ -182,7 +183,9 @@ class LaterController extends MultiSelectController { 'videoItem': item, 'heroTag': heroTag, 'sourceType': 'watchLater', - 'count': (loadingState.value as Success).response.length, + 'count': list.length, + 'favTitle': '稍后再看', + 'mediaId': GStorage.userInfo.get('userInfoCache')?.mid, }, ); break; diff --git a/lib/pages/member/new/content/member_contribute/content/video/member_video.dart b/lib/pages/member/new/content/member_contribute/content/video/member_video.dart index d2c8e6f5..e00fa0c6 100644 --- a/lib/pages/member/new/content/member_contribute/content/video/member_video.dart +++ b/lib/pages/member/new/content/member_contribute/content/video/member_video.dart @@ -6,6 +6,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart' show ContributeType; +import 'package:PiliPalaX/pages/member/new/controller.dart'; import 'package:PiliPalaX/pages/video/detail/reply/view.dart'; import 'package:PiliPalaX/utils/grid.dart'; import 'package:flutter/material.dart'; @@ -19,6 +20,7 @@ class MemberVideo extends StatefulWidget { required this.mid, this.seasonId, this.seriesId, + this.title, }); final ContributeType type; @@ -26,6 +28,7 @@ class MemberVideo extends StatefulWidget { final int mid; final int? seasonId; final int? seriesId; + final String? title; @override State createState() => _MemberVideoState(); @@ -42,6 +45,8 @@ class _MemberVideoState extends State mid: widget.mid, seasonId: widget.seasonId, seriesId: widget.seriesId, + username: Get.find(tag: widget.heroTag).username, + title: widget.title, ), tag: '${widget.heroTag}${widget.type.name}${widget.seasonId}${widget.seriesId}', @@ -72,7 +77,6 @@ class _MemberVideoState extends State padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), color: Theme.of(context).colorScheme.surface, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx( () => Text( @@ -82,6 +86,33 @@ class _MemberVideoState extends State style: const TextStyle(fontSize: 13), ), ), + if (_controller.episodicButton != null) ...[ + const SizedBox(width: 5), + SizedBox( + height: 35, + child: TextButton.icon( + onPressed: _controller.toViewPlayAll, + icon: Icon( + Icons.play_circle_outline_rounded, + size: 16, + color: + Theme.of(context).colorScheme.secondary, + ), + label: Text( + '播放全部', + style: TextStyle( + fontSize: 13, + color: Theme.of(context) + .colorScheme + .secondary, + ), + ), // TODO: continue playing + // label: Text( + // '${_controller.episodicButton?.text}'), + ), + ), + ], + const Spacer(), SizedBox( height: 35, child: TextButton.icon( @@ -110,7 +141,7 @@ class _MemberVideoState extends State ), ), ), - ) + ), ], ), ), diff --git a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart index 23138c40..a3304257 100644 --- a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart +++ b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart @@ -1,9 +1,14 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/models/space_archive/data.dart'; +import 'package:PiliPalaX/models/space_archive/episodic_button.dart'; +import 'package:PiliPalaX/models/space_archive/item.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart' show ContributeType; +import 'package:PiliPalaX/utils/id_utils.dart'; +import 'package:PiliPalaX/utils/utils.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; class MemberVideoCtr extends CommonController { @@ -12,6 +17,8 @@ class MemberVideoCtr extends CommonController { required this.mid, required this.seasonId, required this.seriesId, + this.username, + this.title, }); ContributeType type; @@ -23,6 +30,9 @@ class MemberVideoCtr extends CommonController { RxString sort = 'desc'.obs; RxInt count = (-1).obs; int? next; + EpisodicButton? episodicButton; + final String? username; + final String? title; @override Future onRefresh() async { @@ -43,6 +53,7 @@ class MemberVideoCtr extends CommonController { @override bool customHandleResponse(Success response) { Data data = response.response; + episodicButton = data.episodicButton; next = data.next; aid = data.item?.lastOrNull?.param; isEnd = @@ -80,4 +91,34 @@ class MemberVideoCtr extends CommonController { loadingState.value = LoadingState.loading(); onRefresh(); } + + void toViewPlayAll() { + if (loadingState.value is Success) { + List list = (loadingState.value as Success).response; + for (Item element in list) { + if (element.firstCid == null) { + continue; + } else { + if (element.bvid != list.first.bvid) { + SmartDialog.showToast('已跳过不支持播放的视频'); + } + final String heroTag = Utils.makeHeroTag(element.bvid); + Get.toNamed( + '/video?bvid=${element.bvid}&cid=${element.firstCid}', + arguments: { + 'videoItem': element, + 'heroTag': heroTag, + 'sourceType': 'archive', + 'mediaId': seasonId ?? seriesId ?? mid, + 'oid': IdUtils.bv2av(element.bvid!), // TODO: continue playing + 'favTitle': + '$username: ${title ?? episodicButton?.text ?? '播放全部'}', + 'count': count.value, + }, + ); + break; + } + } + } + } } diff --git a/lib/pages/member/new/content/member_contribute/member_contribute.dart b/lib/pages/member/new/content/member_contribute/member_contribute.dart index 73e5d5d3..713ff289 100644 --- a/lib/pages/member/new/content/member_contribute/member_contribute.dart +++ b/lib/pages/member/new/content/member_contribute/member_contribute.dart @@ -83,11 +83,13 @@ class _MemberContributeState extends State type: ContributeType.video, heroTag: widget.heroTag, mid: widget.mid, + title: item.title, ), 'charging_video' => MemberVideo( type: ContributeType.charging, heroTag: widget.heroTag, mid: widget.mid, + title: item.title, ), 'article' => MemberArticle( heroTag: widget.heroTag, @@ -99,12 +101,14 @@ class _MemberContributeState extends State heroTag: widget.heroTag, mid: widget.mid, seasonId: item.seasonId, + title: item.title, ), 'series' => MemberVideo( type: ContributeType.series, heroTag: widget.heroTag, mid: widget.mid, seriesId: item.seriesId, + title: item.title, ), _ => Center(child: Text(item.title!)) }, @@ -118,6 +122,7 @@ class _MemberContributeState extends State type: ContributeType.video, heroTag: widget.heroTag, mid: widget.mid, + title: '视频', ); } } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index a789c8ec..47516234 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -244,11 +244,16 @@ class VideoDetailController extends GetxController } // 页面来源 稍后再看 收藏夹 - RxString sourceType = 'normal'.obs; + String sourceType = 'normal'; late RxList mediaList = [].obs; - late RxString watchLaterTitle = ''.obs; - bool get isPlayAll => - sourceType.value == 'watchLater' || sourceType.value == 'fav'; + late String watchLaterTitle = ''; + bool get isPlayAll => ['watchLater', 'fav', 'archive'].contains(sourceType); + int get _mediaType => switch (sourceType) { + 'archive' => 1, + 'watchLater' => 2, + 'fav' => 3, + _ => -1, + }; @override void onInit() { @@ -258,23 +263,24 @@ class VideoDetailController extends GetxController if (keys.isNotEmpty) { if (keys.contains('videoItem')) { var args = Get.arguments['videoItem']; - if (args.pic != null && args.pic != '') { - videoItem['pic'] = args.pic; - } + try { + if (args.pic != null && args.pic != '') { + videoItem['pic'] = args.pic; + } else if (args.cover != null && args.cover != '') { + videoItem['pic'] = args.cover; + } + } catch (_) {} } if (keys.contains('pic')) { videoItem['pic'] = Get.arguments['pic']; } } - sourceType.value = Get.arguments['sourceType'] ?? 'normal'; + sourceType = Get.arguments['sourceType'] ?? 'normal'; - if (sourceType.value == 'watchLater') { - watchLaterTitle.value = '稍后再看'; - fetchMediaList(); - } else if (sourceType.value == 'fav') { - watchLaterTitle.value = Get.arguments['favTitle']; - queryFavVideoList(); + if (sourceType != 'normal') { + watchLaterTitle = Get.arguments['favTitle']; + getMediaList(); } bool defaultShowComment = @@ -322,31 +328,17 @@ class VideoDetailController extends GetxController } } - // 获取稍后再看列表 - Future fetchMediaList() async { - var count = Get.arguments['count']; - var res = await UserHttp.getMediaList( - type: 2, - bizId: userInfo.mid, - ps: count, - ); - if (res['status']) { - mediaList = res['data'].reversed.toList(); - } else { - SmartDialog.showToast(res['msg']); - } - } - - void loadMoreMedia() async { + void getMediaList() async { if (mediaList.length >= Get.arguments['count']) { return; } var res = await UserHttp.getMediaList( - type: 3, + type: _mediaType, bizId: Get.arguments['mediaId'] ?? -1, ps: 20, - oid: mediaList.last.id, - otype: mediaList.last.type ?? 2, + oid: mediaList.isEmpty ? null : mediaList.last.id, + otype: mediaList.isEmpty ? null : mediaList.last.type, + desc: _mediaType == 2 ? false : true, ); if (res['status']) { if (res['data'].isNotEmpty) { @@ -364,10 +356,10 @@ class VideoDetailController extends GetxController (context) => MediaListPanel( mediaList: mediaList, changeMediaList: changeMediaList, - panelTitle: watchLaterTitle.value, + panelTitle: watchLaterTitle, bvid: bvid, count: Get.arguments['count'], - loadMoreMedia: loadMoreMedia, + loadMoreMedia: getMediaList, ), ); } @@ -402,20 +394,6 @@ class VideoDetailController extends GetxController } catch (_) {} } - // 获取收藏夹视频列表 - Future queryFavVideoList() async { - var mediaId = Get.arguments['mediaId']; - var oid = Get.arguments['oid']; - var res = await UserHttp.parseFavVideo( - mediaId: mediaId, - oid: oid, - bvid: bvid, - ); - if (res['status']) { - mediaList.value = res['data']; - } - } - int? _lastPos; double? _blockLimit; List>? _blockSettings; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index eabcadad..73df8214 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -95,7 +95,13 @@ class VideoIntroController extends GetxController preRender = true; var args = Get.arguments['videoItem']; var keys = Get.arguments.keys.toList(); - videoItem!['pic'] = args.pic; + try { + if (args.pic != null && args.pic != '') { + videoItem!['pic'] = args.pic; + } else if (args.cover != null && args.cover != '') { + videoItem!['pic'] = args.cover; + } + } catch (_) {} if (args.title is String) { videoItem!['title'] = args.title; } else { @@ -693,7 +699,7 @@ class VideoIntroController extends GetxController if (videoDetailController.isPlayAll && currentIndex == episodes.length - 2) { - videoDetailCtr.loadMoreMedia(); + videoDetailCtr.getMediaList(); } // 列表循环 diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 70f76be2..139e3f69 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1415,46 +1415,44 @@ class _VideoDetailPageState extends State return Stack( children: [ introPanel(), - Obx( - () => AnimatedPositioned( - duration: const Duration(milliseconds: 400), - curve: Curves.easeInOut, - left: 12, - right: 12, - bottom: MediaQuery.of(context).padding.bottom + 12, - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: videoDetailController.showMediaListPanel, - borderRadius: const BorderRadius.all(Radius.circular(14)), - child: Container( - height: 54, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withOpacity(0.95), - borderRadius: const BorderRadius.all(Radius.circular(14)), - ), - child: Row( - children: [ - const Icon(Icons.playlist_play, size: 24), - const SizedBox(width: 10), - Text( - videoDetailController.watchLaterTitle.value, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - fontWeight: FontWeight.bold, - letterSpacing: 0.2, - ), + AnimatedPositioned( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + left: 12, + right: 12, + bottom: MediaQuery.of(context).padding.bottom + 12, + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: videoDetailController.showMediaListPanel, + borderRadius: const BorderRadius.all(Radius.circular(14)), + child: Container( + height: 54, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .secondaryContainer + .withOpacity(0.95), + borderRadius: const BorderRadius.all(Radius.circular(14)), + ), + child: Row( + children: [ + const Icon(Icons.playlist_play, size: 24), + const SizedBox(width: 10), + Text( + videoDetailController.watchLaterTitle, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + fontWeight: FontWeight.bold, + letterSpacing: 0.2, ), - const Spacer(), - const Icon(Icons.keyboard_arrow_up_rounded, size: 26), - ], - ), + ), + const Spacer(), + const Icon(Icons.keyboard_arrow_up_rounded, size: 26), + ], ), ), ),