diff --git a/README.md b/README.md index a47a9827b..0b6a7f0e7 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ ## feat +- [x] 互动视频 +- [x] 发评反诈 +- [x] 高能进度条 - [x] 滑动跳转预览视频缩略图 - [x] Live Photo - [x] 复制/移动收藏夹/稍后再看视频 diff --git a/lib/http/video.dart b/lib/http/video.dart index c9cad789c..ae8d01cd0 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -977,7 +977,6 @@ class VideoHttp { ); if (res.data['code'] == 0) { dynamic data = res.data['data']; - List subtitlesJson = data['subtitle']['subtitles']; /* [ { @@ -995,10 +994,11 @@ class VideoHttp { */ return { 'status': true, - 'data': subtitlesJson, + 'data': data['subtitle']['subtitles'], 'view_points': data['view_points'], // 'last_play_time': data['last_play_time'], 'last_play_cid': data['last_play_cid'], + 'interaction': data['interaction'], }; } else { return {'status': false, 'data': [], 'msg': res.data['message']}; diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 81937b81b..492ab2f61 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -1846,6 +1846,31 @@ class VideoDetailController extends GetxController }); } + // interactive video + dynamic graphVersion; + Map? steinEdgeInfo; + late final RxBool showSteinEdgeInfo = false.obs; + void getSteinEdgeInfo([edgeId]) async { + steinEdgeInfo = null; + try { + dynamic res = await Request().get( + 'https://api.bilibili.com/x/stein/edgeinfo_v2', + queryParameters: { + 'bvid': bvid, + 'graph_version': graphVersion, + if (edgeId != null) 'edge_id': edgeId, + }, + ); + if (res.data['code'] == 0) { + steinEdgeInfo = res.data['data']; + } else { + debugPrint('getSteinEdgeInfo error: ${res.data['message']}'); + } + } catch (e) { + debugPrint('getSteinEdgeInfo: $e'); + } + } + late bool continuePlayingPart = GStorage.continuePlayingPart; Future _querySubtitles() async { @@ -1854,6 +1879,19 @@ class VideoDetailController extends GetxController // SmartDialog.showToast('查询字幕错误,${res["msg"]}'); // } if (res['status']) { + // interactive video + if (graphVersion == null) { + try { + final introCtr = Get.find(tag: heroTag); + if (introCtr.videoDetail.value.rights?['is_stein_gate'] == 1) { + graphVersion = res['interaction']?['graph_version']; + getSteinEdgeInfo(); + } + } catch (e) { + debugPrint('handle stein: $e'); + } + } + if (continuePlayingPart) { continuePlayingPart = false; try { @@ -1945,7 +1983,7 @@ class VideoDetailController extends GetxController super.onClose(); } - onReset() { + onReset([isStein]) { playedTime = null; videoUrl = null; audioUrl = null; @@ -1968,6 +2006,13 @@ class VideoDetailController extends GetxController segmentList.clear(); _segmentProgressList = null; } + + // interactive video + if (isStein != true) { + graphVersion = null; + } + steinEdgeInfo = null; + showSteinEdgeInfo.value = false; } late final showDmChart = GStorage.showDmChart; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 91347fe05..020e42a92 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -578,13 +578,13 @@ class VideoIntroController extends GetxController } // 修改分P或番剧分集 - Future changeSeasonOrbangu(epid, bvid, cid, aid, cover) async { + Future changeSeasonOrbangu(epid, bvid, cid, aid, cover, [isStein]) async { // 重新获取视频资源 final videoDetailCtr = Get.find(tag: heroTag) ..plPlayerController.pause() ..makeHeartBeat() ..updateMediaListHistory(aid) - ..onReset() + ..onReset(isStein) ..bvid = bvid ..oid.value = aid ?? IdUtils.bv2av(bvid) ..cid.value = cid diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index eab1ace56..53ded2562 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -240,6 +240,15 @@ class _VideoDetailPageState extends State // 播放器状态监听 void playerListener(PlayerStatus? status) async { if (status == PlayerStatus.completed) { + try { + if ((videoDetailController.steinEdgeInfo?['edges']['questions'][0] + ['choices'] as List?) + ?.isNotEmpty == + true) { + videoDetailController.showSteinEdgeInfo.value = true; + return; + } + } catch (_) {} shutdownTimerService.handleWaitingFinished(); bool notExitFlag = false; @@ -1421,6 +1430,69 @@ class _VideoDetailPageState extends State // child: Text('index'), // ), // ), + + Obx( + () { + if (videoDetailController.showSteinEdgeInfo.value) { + try { + return Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: plPlayerController?.showControls.value == true + ? 75 + : 16, + ), + child: Wrap( + spacing: 25, + runSpacing: 10, + children: (videoDetailController.steinEdgeInfo!['edges'] + ['questions'][0]['choices'] as List) + .map((item) { + return FilledButton.tonal( + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + backgroundColor: Theme.of(context) + .colorScheme + .secondaryContainer + .withOpacity(0.8), + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + visualDensity: + VisualDensity(horizontal: -2, vertical: -2), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () { + videoIntroController.changeSeasonOrbangu( + null, + videoDetailController.bvid, + item['cid'], + IdUtils.bv2av(videoDetailController.bvid), + null, + true, + ); + videoDetailController + .getSteinEdgeInfo(item['id']); + }, + child: Text(item['option']), + ); + }).toList(), + ), + ), + ); + } catch (e) { + debugPrint('build stein edges: $e'); + return const SizedBox.shrink(); + } + } + return const SizedBox.shrink(); + }, + ), ], );