mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-22 18:20:14 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31a639400e | ||
|
|
d6b24561fa | ||
|
|
7ba9646d38 | ||
|
|
58a7cf1e75 | ||
|
|
1a327198f7 | ||
|
|
e4fe91ef92 | ||
|
|
afcf817c4f |
10
README.md
10
README.md
@@ -47,6 +47,14 @@
|
|||||||
|
|
||||||
## feat
|
## feat
|
||||||
|
|
||||||
|
- [x] 滑动跳转预览视频缩略图
|
||||||
|
- [x] Live Photo
|
||||||
|
- [x] 复制/移动收藏夹/稍后再看视频
|
||||||
|
- [x] 超分辨率
|
||||||
|
- [x] 合并弹幕
|
||||||
|
- [x] 会员彩色弹幕
|
||||||
|
- [x] 播放全部/继续播放/倒序播放
|
||||||
|
- [x] Cookie登录
|
||||||
- [x] 显示视频分段信息
|
- [x] 显示视频分段信息
|
||||||
- [x] 调节字幕大小
|
- [x] 调节字幕大小
|
||||||
- [x] 调节全屏弹幕大小
|
- [x] 调节全屏弹幕大小
|
||||||
@@ -74,7 +82,7 @@
|
|||||||
- [x] 转发动态
|
- [x] 转发动态
|
||||||
- [x] 合集图片
|
- [x] 合集图片
|
||||||
- [x] 删除/置顶私信
|
- [x] 删除/置顶私信
|
||||||
- [x] 举报用户/评论/视频
|
- [x] 举报用户/评论/视频/动态
|
||||||
- [x] 删除/发布文本/图片动态
|
- [x] 删除/发布文本/图片动态
|
||||||
- [x] 其他
|
- [x] 其他
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
|
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
|
||||||
? '${widget.sources[index]}@${_quality}q.webp'.http2https
|
? '${widget.sources[index].url}@${_quality}q.webp'.http2https
|
||||||
: widget.sources[index].url.http2https;
|
: widget.sources[index].url.http2https;
|
||||||
|
|
||||||
void onClose() {
|
void onClose() {
|
||||||
|
|||||||
@@ -418,11 +418,7 @@ class BangumiIntroController extends CommonController {
|
|||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
||||||
..plPlayerController.pause()
|
..plPlayerController.pause()
|
||||||
..makeHeartBeat()
|
..makeHeartBeat()
|
||||||
..playedTime = null
|
..onReset()
|
||||||
..videoUrl = null
|
|
||||||
..audioUrl = null
|
|
||||||
..vttSubtitlesIndex = null
|
|
||||||
..savedDanmaku = null
|
|
||||||
..epId = epId
|
..epId = epId
|
||||||
..bvid = bvid
|
..bvid = bvid
|
||||||
..cid.value = cid
|
..cid.value = cid
|
||||||
|
|||||||
@@ -1929,6 +1929,14 @@ List<SettingsModel> get extraSettings => [
|
|||||||
setKey: SettingBoxKey.showSeekPreview,
|
setKey: SettingBoxKey.showSeekPreview,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
|
SettingsModel(
|
||||||
|
settingsType: SettingsType.sw1tch,
|
||||||
|
title: '显示高能进度条',
|
||||||
|
subtitle: '高能进度条反应了在时域上,单位时间内弹幕发送量的变化趋势',
|
||||||
|
leading: Icon(Icons.show_chart),
|
||||||
|
setKey: SettingBoxKey.showDmChart,
|
||||||
|
defaultVal: false,
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
|
|||||||
@@ -635,6 +635,9 @@ class VideoDetailController extends GetxController
|
|||||||
|
|
||||||
Future _querySponsorBlock() async {
|
Future _querySponsorBlock() async {
|
||||||
positionSubscription?.cancel();
|
positionSubscription?.cancel();
|
||||||
|
videoLabel.value = '';
|
||||||
|
segmentList.clear();
|
||||||
|
_segmentProgressList = null;
|
||||||
dynamic result = await Request().get(
|
dynamic result = await Request().get(
|
||||||
'${GStorage.blockServer}/api/skipSegments',
|
'${GStorage.blockServer}/api/skipSegments',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
@@ -643,9 +646,6 @@ class VideoDetailController extends GetxController
|
|||||||
},
|
},
|
||||||
options: _options,
|
options: _options,
|
||||||
);
|
);
|
||||||
videoLabel.value = '';
|
|
||||||
segmentList.clear();
|
|
||||||
_segmentProgressList = null;
|
|
||||||
_handleSBData(result);
|
_handleSBData(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,6 +1004,7 @@ class VideoDetailController extends GetxController
|
|||||||
vttSubtitles: _vttSubtitles,
|
vttSubtitles: _vttSubtitles,
|
||||||
vttSubtitlesIndex: vttSubtitlesIndex,
|
vttSubtitlesIndex: vttSubtitlesIndex,
|
||||||
showVP: showVP,
|
showVP: showVP,
|
||||||
|
dmTrend: dmTrend,
|
||||||
// 硬解
|
// 硬解
|
||||||
enableHA: enableHA.value,
|
enableHA: enableHA.value,
|
||||||
hwdec: hwdec.value,
|
hwdec: hwdec.value,
|
||||||
@@ -1037,6 +1038,10 @@ class VideoDetailController extends GetxController
|
|||||||
_getSubtitle();
|
_getSubtitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showDmChart && dmTrend == null) {
|
||||||
|
_getDmTrend();
|
||||||
|
}
|
||||||
|
|
||||||
/// 开启自动全屏时,在player初始化完成后立即传入headerControl
|
/// 开启自动全屏时,在player初始化完成后立即传入headerControl
|
||||||
plPlayerController.headerControl = headerControl;
|
plPlayerController.headerControl = headerControl;
|
||||||
|
|
||||||
@@ -1359,7 +1364,8 @@ class VideoDetailController extends GetxController
|
|||||||
duration += split[i] * pow(60, i).toInt();
|
duration += split[i] * pow(60, i).toInt();
|
||||||
}
|
}
|
||||||
if (duration <=
|
if (duration <=
|
||||||
plPlayerController.durationSeconds.value) {
|
plPlayerController
|
||||||
|
.durationSeconds.value.inSeconds) {
|
||||||
setState(() {
|
setState(() {
|
||||||
updateSegment(
|
updateSegment(
|
||||||
isFirst: isFirst,
|
isFirst: isFirst,
|
||||||
@@ -1687,7 +1693,9 @@ class VideoDetailController extends GetxController
|
|||||||
'userID': GStorage.blockUserID,
|
'userID': GStorage.blockUserID,
|
||||||
'userAgent': Constants.userAgent,
|
'userAgent': Constants.userAgent,
|
||||||
'videoDuration': plPlayerController
|
'videoDuration': plPlayerController
|
||||||
.durationSeconds.value,
|
.durationSeconds
|
||||||
|
.value
|
||||||
|
.inSeconds,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
'segments': list!
|
'segments': list!
|
||||||
@@ -1960,4 +1968,54 @@ class VideoDetailController extends GetxController
|
|||||||
tabCtr.dispose();
|
tabCtr.dispose();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onReset() {
|
||||||
|
playedTime = null;
|
||||||
|
videoUrl = null;
|
||||||
|
audioUrl = null;
|
||||||
|
|
||||||
|
// danmaku
|
||||||
|
dmTrend = null;
|
||||||
|
savedDanmaku = null;
|
||||||
|
|
||||||
|
// subtitle
|
||||||
|
vttSubtitlesIndex = null;
|
||||||
|
_vttSubtitles.clear();
|
||||||
|
|
||||||
|
// view point
|
||||||
|
viewPointList.clear();
|
||||||
|
|
||||||
|
// sponsor block
|
||||||
|
positionSubscription?.cancel();
|
||||||
|
videoLabel.value = '';
|
||||||
|
segmentList.clear();
|
||||||
|
_segmentProgressList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
late final showDmChart = GStorage.showDmChart;
|
||||||
|
List? dmTrend;
|
||||||
|
|
||||||
|
void _getDmTrend() async {
|
||||||
|
dmTrend = [];
|
||||||
|
try {
|
||||||
|
dynamic res = await Request().get(
|
||||||
|
'https://bvc.bilivideo.com/pbp/data',
|
||||||
|
queryParameters: {
|
||||||
|
'bvid': bvid,
|
||||||
|
'cid': cid.value,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
int stepSec = (res.data['step_sec'] as num?)?.toInt() ?? 0;
|
||||||
|
late List events = (res.data['events']['default'] as List?) ?? [];
|
||||||
|
if (stepSec != 0 && events.isNotEmpty) {
|
||||||
|
dmTrend = events;
|
||||||
|
if (plPlayerController.dmTrend.isEmpty) {
|
||||||
|
plPlayerController.dmTrend.value = events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('_getDmTrend: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -583,12 +583,8 @@ class VideoIntroController extends GetxController
|
|||||||
final videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag)
|
final videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag)
|
||||||
..plPlayerController.pause()
|
..plPlayerController.pause()
|
||||||
..makeHeartBeat()
|
..makeHeartBeat()
|
||||||
..playedTime = null
|
|
||||||
..videoUrl = null
|
|
||||||
..audioUrl = null
|
|
||||||
..updateMediaListHistory(aid)
|
..updateMediaListHistory(aid)
|
||||||
..vttSubtitlesIndex = null
|
..onReset()
|
||||||
..savedDanmaku = null
|
|
||||||
..bvid = bvid
|
..bvid = bvid
|
||||||
..oid.value = aid ?? IdUtils.bv2av(bvid)
|
..oid.value = aid ?? IdUtils.bv2av(bvid)
|
||||||
..cid.value = cid
|
..cid.value = cid
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class PlPlayerController {
|
|||||||
// 展示使用
|
// 展示使用
|
||||||
final Rx<Duration> _sliderTempPosition = Rx(Duration.zero);
|
final Rx<Duration> _sliderTempPosition = Rx(Duration.zero);
|
||||||
final Rx<Duration> _duration = Rx(Duration.zero);
|
final Rx<Duration> _duration = Rx(Duration.zero);
|
||||||
final RxInt durationSeconds = 0.obs;
|
final Rx<Duration> durationSeconds = Duration.zero.obs;
|
||||||
final Rx<Duration> _buffered = Rx(Duration.zero);
|
final Rx<Duration> _buffered = Rx(Duration.zero);
|
||||||
final RxInt bufferedSeconds = 0.obs;
|
final RxInt bufferedSeconds = 0.obs;
|
||||||
|
|
||||||
@@ -330,9 +330,8 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateDurationSecond() {
|
void updateDurationSecond() {
|
||||||
int newSecond = _duration.value.inSeconds;
|
if (durationSeconds.value != _duration.value) {
|
||||||
if (durationSeconds.value != newSecond) {
|
durationSeconds.value = _duration.value;
|
||||||
durationSeconds.value = newSecond;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +461,7 @@ class PlPlayerController {
|
|||||||
List<Map<String, String>>? vttSubtitles,
|
List<Map<String, String>>? vttSubtitles,
|
||||||
int? vttSubtitlesIndex,
|
int? vttSubtitlesIndex,
|
||||||
bool? showVP,
|
bool? showVP,
|
||||||
|
List? dmTrend,
|
||||||
bool autoplay = true,
|
bool autoplay = true,
|
||||||
// 默认不循环
|
// 默认不循环
|
||||||
PlaylistMode looping = PlaylistMode.none,
|
PlaylistMode looping = PlaylistMode.none,
|
||||||
@@ -494,6 +494,7 @@ class PlPlayerController {
|
|||||||
this.vttSubtitles.value = vttSubtitles ?? <Map<String, String>>[];
|
this.vttSubtitles.value = vttSubtitles ?? <Map<String, String>>[];
|
||||||
this.vttSubtitlesIndex.value = vttSubtitlesIndex ?? 0;
|
this.vttSubtitlesIndex.value = vttSubtitlesIndex ?? 0;
|
||||||
this.showVP.value = showVP ?? true;
|
this.showVP.value = showVP ?? true;
|
||||||
|
this.dmTrend.value = dmTrend ?? [];
|
||||||
_autoPlay = autoplay;
|
_autoPlay = autoplay;
|
||||||
_looping = looping;
|
_looping = looping;
|
||||||
// 初始化视频倍速
|
// 初始化视频倍速
|
||||||
@@ -1437,7 +1438,7 @@ class PlPlayerController {
|
|||||||
}
|
}
|
||||||
bool isComplete = playerStatus.status.value == PlayerStatus.completed ||
|
bool isComplete = playerStatus.status.value == PlayerStatus.completed ||
|
||||||
type == 'completed';
|
type == 'completed';
|
||||||
if ((duration.value - position.value).inMilliseconds > 1000) {
|
if ((durationSeconds.value - position.value).inMilliseconds > 1000) {
|
||||||
isComplete = false;
|
isComplete = false;
|
||||||
}
|
}
|
||||||
// 播放状态变化时,更新
|
// 播放状态变化时,更新
|
||||||
@@ -1582,23 +1583,30 @@ class PlPlayerController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_isQueryingVideoShot = true;
|
_isQueryingVideoShot = true;
|
||||||
dynamic res = await Request().get(
|
try {
|
||||||
'https://api.bilibili.com/x/player/videoshot',
|
dynamic res = await Request().get(
|
||||||
queryParameters: {
|
'https://api.bilibili.com/x/player/videoshot',
|
||||||
// 'aid': IdUtils.bv2av(_bvid),
|
queryParameters: {
|
||||||
'bvid': _bvid,
|
// 'aid': IdUtils.bv2av(_bvid),
|
||||||
'cid': _cid,
|
'bvid': _bvid,
|
||||||
'index': 1,
|
'cid': _cid,
|
||||||
},
|
'index': 1,
|
||||||
);
|
},
|
||||||
if (res.data['code'] == 0) {
|
);
|
||||||
videoShot = {
|
if (res.data['code'] == 0) {
|
||||||
'status': true,
|
videoShot = {
|
||||||
'data': res.data['data'],
|
'status': true,
|
||||||
};
|
'data': res.data['data'],
|
||||||
} else {
|
};
|
||||||
videoShot = {'status': false};
|
} else {
|
||||||
|
videoShot = {'status': false};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('getVideoShot: $e');
|
||||||
}
|
}
|
||||||
_isQueryingVideoShot = false;
|
_isQueryingVideoShot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late final RxList dmTrend = [].obs;
|
||||||
|
late final RxBool showDmChart = true.obs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ enum BottomControlType {
|
|||||||
custom,
|
custom,
|
||||||
viewPoints,
|
viewPoints,
|
||||||
superResolution,
|
superResolution,
|
||||||
|
dmChart,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/utils/extension.dart';
|
|||||||
import 'package:PiliPlus/utils/id_utils.dart';
|
import 'package:PiliPlus/utils/id_utils.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
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:flutter_volume_controller/flutter_volume_controller.dart';
|
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
||||||
@@ -321,7 +322,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}),
|
}),
|
||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
Utils.timeFormat(plPlayerController.durationSeconds.value),
|
Utils.timeFormat(
|
||||||
|
plPlayerController.durationSeconds.value.inSeconds),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFFD0D0D0),
|
color: Color(0xFFD0D0D0),
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
@@ -329,7 +331,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
fontFeatures: [FontFeature.tabularFigures()],
|
fontFeatures: [FontFeature.tabularFigures()],
|
||||||
),
|
),
|
||||||
semanticsLabel:
|
semanticsLabel:
|
||||||
'共${Utils.durationReadFormat(Utils.timeFormat(plPlayerController.durationSeconds.value))}',
|
'共${Utils.durationReadFormat(Utils.timeFormat(plPlayerController.durationSeconds.value.inSeconds))}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -338,7 +340,43 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
/// 空白占位
|
/// 空白占位
|
||||||
BottomControlType.space: const Spacer(),
|
BottomControlType.space: const Spacer(),
|
||||||
|
|
||||||
/// 分段信息
|
/// 高能进度条
|
||||||
|
BottomControlType.dmChart: Obx(() => plPlayerController.dmTrend.isEmpty
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Container(
|
||||||
|
width: widgetWidth,
|
||||||
|
height: 30,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: ComBtn(
|
||||||
|
icon: plPlayerController.showDmChart.value
|
||||||
|
? Icon(
|
||||||
|
Icons.show_chart,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
)
|
||||||
|
: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.show_chart,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.hide_source,
|
||||||
|
size: 22,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fuc: () {
|
||||||
|
plPlayerController.showDmChart.value =
|
||||||
|
!plPlayerController.showDmChart.value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
|
||||||
|
/// 超分辨率
|
||||||
BottomControlType.superResolution: Get.parameters['type'] == '1' ||
|
BottomControlType.superResolution: Get.parameters['type'] == '1' ||
|
||||||
Get.parameters['type'] == '4'
|
Get.parameters['type'] == '4'
|
||||||
? Container(
|
? Container(
|
||||||
@@ -519,8 +557,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
width: 35,
|
width: 35,
|
||||||
height: 30,
|
height: 30,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.closed_caption_off_outlined,
|
plPlayerController.vttSubtitlesIndex.value == 0
|
||||||
|
? Icons.closed_caption_off_outlined
|
||||||
|
: Icons.closed_caption_off_rounded,
|
||||||
size: 22,
|
size: 22,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
semanticLabel: '字幕',
|
semanticLabel: '字幕',
|
||||||
@@ -585,6 +625,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
if (anySeason) BottomControlType.pre,
|
if (anySeason) BottomControlType.pre,
|
||||||
if (anySeason) BottomControlType.next,
|
if (anySeason) BottomControlType.next,
|
||||||
BottomControlType.space,
|
BottomControlType.space,
|
||||||
|
BottomControlType.dmChart,
|
||||||
BottomControlType.superResolution,
|
BottomControlType.superResolution,
|
||||||
BottomControlType.viewPoints,
|
BottomControlType.viewPoints,
|
||||||
if (anySeason) BottomControlType.episode,
|
if (anySeason) BottomControlType.episode,
|
||||||
@@ -702,7 +743,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
if (plPlayerController.showSeekPreview) {
|
if (plPlayerController.showSeekPreview) {
|
||||||
try {
|
try {
|
||||||
plPlayerController.previewDx.value = result.inMilliseconds /
|
plPlayerController.previewDx.value = result.inMilliseconds /
|
||||||
plPlayerController.duration.value.inMilliseconds *
|
plPlayerController
|
||||||
|
.durationSeconds.value.inMilliseconds *
|
||||||
context.size!.width;
|
context.size!.width;
|
||||||
if (plPlayerController.showPreview.value.not) {
|
if (plPlayerController.showPreview.value.not) {
|
||||||
plPlayerController.showPreview.value = true;
|
plPlayerController.showPreview.value = true;
|
||||||
@@ -856,11 +898,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
plPlayerController.duration.value.inMinutes >= 60
|
plPlayerController
|
||||||
|
.durationSeconds.value.inMinutes >=
|
||||||
|
60
|
||||||
? printDurationWithHours(
|
? printDurationWithHours(
|
||||||
plPlayerController.duration.value)
|
plPlayerController.durationSeconds.value)
|
||||||
: printDuration(
|
: printDuration(
|
||||||
plPlayerController.duration.value),
|
plPlayerController.durationSeconds.value),
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1067,11 +1111,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
|
|
||||||
/// 进度条 live模式下禁用
|
/// 进度条 live模式下禁用
|
||||||
|
|
||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final int value = plPlayerController.sliderPositionSeconds.value;
|
final int value = plPlayerController.sliderPositionSeconds.value;
|
||||||
final int max = plPlayerController.durationSeconds.value;
|
final int max = plPlayerController.durationSeconds.value.inSeconds;
|
||||||
final int buffer = plPlayerController.bufferedSeconds.value;
|
final int buffer = plPlayerController.bufferedSeconds.value;
|
||||||
if (plPlayerController.showControls.value) {
|
if (plPlayerController.showControls.value) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
@@ -1108,37 +1151,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
children: [
|
children: [
|
||||||
|
if (plPlayerController.dmTrend.isNotEmpty &&
|
||||||
|
plPlayerController.showDmChart.value)
|
||||||
|
buildDmChart(context, plPlayerController),
|
||||||
if (plPlayerController.viewPointList.isNotEmpty &&
|
if (plPlayerController.viewPointList.isNotEmpty &&
|
||||||
plPlayerController.showVP.value)
|
plPlayerController.showVP.value)
|
||||||
LayoutBuilder(
|
buildViewPointWidget(plPlayerController),
|
||||||
builder: (context, constraints) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 20,
|
|
||||||
child: Listener(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onPointerDown: (event) {
|
|
||||||
try {
|
|
||||||
double seg = event.localPosition.dx /
|
|
||||||
constraints.maxWidth;
|
|
||||||
Segment item = plPlayerController
|
|
||||||
.viewPointList
|
|
||||||
.where((item) {
|
|
||||||
return item.start >= seg;
|
|
||||||
}).reduce((a, b) =>
|
|
||||||
a.start < b.start ? a : b);
|
|
||||||
if (item.from != null) {
|
|
||||||
plPlayerController.seekTo(
|
|
||||||
Duration(seconds: item.from!));
|
|
||||||
}
|
|
||||||
// debugPrint('${item.title},,${item.from}');
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('$e');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ProgressBar(
|
ProgressBar(
|
||||||
progress: Duration(seconds: value),
|
progress: Duration(seconds: value),
|
||||||
buffered: Duration(seconds: buffer),
|
buffered: Duration(seconds: buffer),
|
||||||
@@ -1499,6 +1517,57 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildDmChart(
|
||||||
|
BuildContext context,
|
||||||
|
PlPlayerController plPlayerController, [
|
||||||
|
double offset = 0,
|
||||||
|
]) {
|
||||||
|
return IgnorePointer(
|
||||||
|
child: Container(
|
||||||
|
height: 14,
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
bottom: plPlayerController.viewPointList.isNotEmpty &&
|
||||||
|
plPlayerController.showVP.value
|
||||||
|
? 20.25 + offset
|
||||||
|
: 4.25 + offset,
|
||||||
|
),
|
||||||
|
child: LineChart(
|
||||||
|
LineChartData(
|
||||||
|
titlesData: const FlTitlesData(show: false),
|
||||||
|
lineTouchData: const LineTouchData(enabled: false),
|
||||||
|
gridData: const FlGridData(show: false),
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
// minX: 0,
|
||||||
|
// maxX: (plPlayerController.dmTrend.length - 1).toDouble(),
|
||||||
|
// minY: 0,
|
||||||
|
// maxY: plPlayerController.dmTrend
|
||||||
|
// .reduce((a, b) => a > b ? a : b)
|
||||||
|
// .toDouble(),
|
||||||
|
lineBarsData: [
|
||||||
|
LineChartBarData(
|
||||||
|
spots: List.generate(
|
||||||
|
plPlayerController.dmTrend.length,
|
||||||
|
(index) => FlSpot(
|
||||||
|
index.toDouble(),
|
||||||
|
plPlayerController.dmTrend[index].toDouble(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
isCurved: true,
|
||||||
|
barWidth: 1,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
dotData: const FlDotData(show: false),
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: true,
|
||||||
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (plPlayerController.showPreview.value.not) {
|
if (plPlayerController.showPreview.value.not) {
|
||||||
@@ -1601,3 +1670,30 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildViewPointWidget(PlPlayerController plPlayerController) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 20,
|
||||||
|
child: Listener(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onPointerDown: (event) {
|
||||||
|
try {
|
||||||
|
double seg = event.localPosition.dx / constraints.maxWidth;
|
||||||
|
Segment item = plPlayerController.viewPointList.where((item) {
|
||||||
|
return item.start >= seg;
|
||||||
|
}).reduce((a, b) => a.start < b.start ? a : b);
|
||||||
|
if (item.from != null) {
|
||||||
|
plPlayerController.seekTo(Duration(seconds: item.from!));
|
||||||
|
}
|
||||||
|
// debugPrint('${item.title},,${item.from}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('$e');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:nil/nil.dart';
|
import 'package:nil/nil.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/index.dart'
|
import 'package:PiliPlus/plugin/pl_player/index.dart'
|
||||||
show PlPlayerController, buildSeekPreviewWidget;
|
show
|
||||||
|
PlPlayerController,
|
||||||
|
buildSeekPreviewWidget,
|
||||||
|
buildDmChart,
|
||||||
|
buildViewPointWidget;
|
||||||
import 'package:PiliPlus/utils/feed_back.dart';
|
import 'package:PiliPlus/utils/feed_back.dart';
|
||||||
|
|
||||||
import '../../../common/widgets/audio_video_progress_bar.dart';
|
import '../../../common/widgets/audio_video_progress_bar.dart';
|
||||||
@@ -38,7 +42,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
final int value = controller!.sliderPositionSeconds.value;
|
final int value = controller!.sliderPositionSeconds.value;
|
||||||
final int max = controller!.durationSeconds.value;
|
final int max = controller!.durationSeconds.value.inSeconds;
|
||||||
final int buffer = controller!.bufferedSeconds.value;
|
final int buffer = controller!.bufferedSeconds.value;
|
||||||
if (value > max || max <= 0) {
|
if (value > max || max <= 0) {
|
||||||
return nil;
|
return nil;
|
||||||
@@ -53,36 +57,14 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
children: [
|
children: [
|
||||||
|
if (controller?.dmTrend.isNotEmpty == true &&
|
||||||
|
controller?.showDmChart.value == true)
|
||||||
|
buildDmChart(context, controller!, 4.5),
|
||||||
if (controller?.viewPointList.isNotEmpty == true &&
|
if (controller?.viewPointList.isNotEmpty == true &&
|
||||||
controller?.showVP.value == true)
|
controller?.showVP.value == true)
|
||||||
LayoutBuilder(
|
Padding(
|
||||||
builder: (context, constraints) {
|
padding: const EdgeInsets.only(bottom: 5.25),
|
||||||
return Container(
|
child: buildViewPointWidget(controller!),
|
||||||
height: 20,
|
|
||||||
margin: const EdgeInsets.only(bottom: 5.25),
|
|
||||||
child: Listener(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onPointerDown: (event) {
|
|
||||||
try {
|
|
||||||
double seg = event.localPosition.dx /
|
|
||||||
constraints.maxWidth;
|
|
||||||
Segment? item = controller?.viewPointList
|
|
||||||
.where((item) {
|
|
||||||
return item.start >= seg;
|
|
||||||
}).reduce((a, b) =>
|
|
||||||
a.start < b.start ? a : b);
|
|
||||||
if (item?.from != null) {
|
|
||||||
controller?.seekTo(
|
|
||||||
Duration(seconds: item!.from!));
|
|
||||||
}
|
|
||||||
// debugPrint('${item?.title},,${item?.from}');
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('$e');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ProgressBar(
|
ProgressBar(
|
||||||
progress: Duration(seconds: value),
|
progress: Duration(seconds: value),
|
||||||
|
|||||||
@@ -360,6 +360,9 @@ class GStorage {
|
|||||||
static bool get showSeekPreview =>
|
static bool get showSeekPreview =>
|
||||||
GStorage.setting.get(SettingBoxKey.showSeekPreview, defaultValue: true);
|
GStorage.setting.get(SettingBoxKey.showSeekPreview, defaultValue: true);
|
||||||
|
|
||||||
|
static bool get showDmChart =>
|
||||||
|
GStorage.setting.get(SettingBoxKey.showDmChart, defaultValue: false);
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -589,6 +592,7 @@ class SettingBoxKey {
|
|||||||
showDynDecorate = 'showDynDecorate',
|
showDynDecorate = 'showDynDecorate',
|
||||||
enableLivePhoto = 'enableLivePhoto',
|
enableLivePhoto = 'enableLivePhoto',
|
||||||
showSeekPreview = 'showSeekPreview',
|
showSeekPreview = 'showSeekPreview',
|
||||||
|
showDmChart = 'showDmChart',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@@ -473,6 +473,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.3"
|
version: "5.0.3"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
expandable:
|
expandable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -570,6 +578,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
fl_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_chart
|
||||||
|
sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.69.2"
|
||||||
flex_seed_scheme:
|
flex_seed_scheme:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ dependencies:
|
|||||||
expandable: ^5.0.1
|
expandable: ^5.0.1
|
||||||
flex_seed_scheme: ^3.4.1
|
flex_seed_scheme: ^3.4.1
|
||||||
live_photo_maker: ^0.0.6
|
live_photo_maker: ^0.0.6
|
||||||
|
fl_chart: ^0.69.2
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
screen_brightness: ^2.0.1
|
screen_brightness: ^2.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user