mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: video subtitle
avoid refetching subtitle fix stuck when parsing large subtitle body opt: viewpoints Update README.md Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -25,8 +25,6 @@ import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../models/video/play/subtitle.dart';
|
||||
|
||||
Box videoStorage = GStorage.video;
|
||||
Box setting = GStorage.setting;
|
||||
Box localCache = GStorage.localCache;
|
||||
@@ -104,8 +102,9 @@ class PlPlayerController {
|
||||
bool _enableHeart = true;
|
||||
|
||||
late DataSource dataSource;
|
||||
final RxList<Map<String, String>> _vttSubtitles = <Map<String, String>>[].obs;
|
||||
final RxInt _vttSubtitlesIndex = 0.obs;
|
||||
// 视频字幕
|
||||
final RxList<Map<String, String>> vttSubtitles = <Map<String, String>>[].obs;
|
||||
final RxInt vttSubtitlesIndex = 0.obs;
|
||||
|
||||
Timer? _timer;
|
||||
Timer? _timerForSeek;
|
||||
@@ -115,6 +114,7 @@ class PlPlayerController {
|
||||
Timer? timerForTrackingMouse;
|
||||
|
||||
final RxList<Segment> viewPointList = <Segment>[].obs;
|
||||
final RxBool showVP = true.obs;
|
||||
final RxList<Segment> segmentList = <Segment>[].obs;
|
||||
|
||||
// final Durations durations;
|
||||
@@ -164,10 +164,6 @@ class PlPlayerController {
|
||||
Rx<bool> get mute => _mute;
|
||||
Stream<bool> get onMuteChanged => _mute.stream;
|
||||
|
||||
// 视频字幕
|
||||
RxList<Map<String, String>> get vttSubtitles => _vttSubtitles;
|
||||
RxInt get vttSubtitlesIndex => _vttSubtitlesIndex;
|
||||
|
||||
/// [videoPlayerController] instance of Player
|
||||
Player? get videoPlayerController => _videoPlayerController;
|
||||
|
||||
@@ -408,6 +404,9 @@ class PlPlayerController {
|
||||
Future<void> setDataSource(
|
||||
DataSource dataSource, {
|
||||
List<Segment>? segmentList,
|
||||
List<Segment>? viewPointList,
|
||||
List<Map<String, String>>? vttSubtitles,
|
||||
int? vttSubtitlesIndex,
|
||||
bool autoplay = true,
|
||||
// 默认不循环
|
||||
PlaylistMode looping = PlaylistMode.none,
|
||||
@@ -431,8 +430,10 @@ class PlPlayerController {
|
||||
}) async {
|
||||
try {
|
||||
this.dataSource = dataSource;
|
||||
viewPointList.clear();
|
||||
this.segmentList.value = segmentList ?? <Segment>[];
|
||||
this.viewPointList.value = viewPointList ?? <Segment>[];
|
||||
this.vttSubtitles.value = vttSubtitles ?? <Map<String, String>>[];
|
||||
this.vttSubtitlesIndex.value = vttSubtitlesIndex ?? 0;
|
||||
_autoPlay = autoplay;
|
||||
_looping = looping;
|
||||
// 初始化视频倍速
|
||||
@@ -467,35 +468,7 @@ class PlPlayerController {
|
||||
startListeners();
|
||||
}
|
||||
await _initializePlayer(seekTo: seekTo);
|
||||
if (videoType.value != 'live' && _cid != 0) {
|
||||
refreshSubtitles().then((value) {
|
||||
if (_vttSubtitles.isNotEmpty) {
|
||||
if (_vttSubtitlesIndex > 0 &&
|
||||
_vttSubtitlesIndex < _vttSubtitles.length) {
|
||||
setSubtitle(_vttSubtitlesIndex.value);
|
||||
} else {
|
||||
String preference = setting.get(SettingBoxKey.subtitlePreference,
|
||||
defaultValue: SubtitlePreference.values.first.code);
|
||||
if (preference == 'on') {
|
||||
setSubtitle(1);
|
||||
} else if (preference == 'withoutAi') {
|
||||
bool found = false;
|
||||
for (int i = 1; i < _vttSubtitles.length; i++) {
|
||||
if (_vttSubtitles[i]['language']!.startsWith('ai')) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
setSubtitle(i);
|
||||
break;
|
||||
}
|
||||
if (!found) _vttSubtitlesIndex.value = 0;
|
||||
} else {
|
||||
_vttSubtitlesIndex.value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
setSubtitle(this.vttSubtitlesIndex.value);
|
||||
} catch (err, stackTrace) {
|
||||
dataStatus.status.value = DataStatus.error;
|
||||
debugPrint(stackTrace.toString());
|
||||
@@ -1353,49 +1326,20 @@ class PlPlayerController {
|
||||
}
|
||||
}
|
||||
|
||||
Future refreshSubtitles() async {
|
||||
_vttSubtitles.clear();
|
||||
Map res = await VideoHttp.subtitlesJson(bvid: _bvid, cid: _cid);
|
||||
// if (!res["status"]) {
|
||||
// SmartDialog.showToast('查询字幕错误,${res["msg"]}');
|
||||
// }
|
||||
|
||||
if (res["data"] is List && res["data"].isNotEmpty) {
|
||||
var result = await VideoHttp.vttSubtitles(res["data"]);
|
||||
if (result != null) {
|
||||
_vttSubtitles.value = result;
|
||||
}
|
||||
// if (_vttSubtitles.isEmpty) {
|
||||
// SmartDialog.showToast('字幕均加载失败');
|
||||
// }
|
||||
}
|
||||
if (res["view_points"] is List && res["view_points"].isNotEmpty) {
|
||||
viewPointList.value = (res["view_points"] as List).map((item) {
|
||||
double start = (item['to'] / durationSeconds.value).clamp(0.0, 1.0);
|
||||
return Segment(
|
||||
start,
|
||||
start,
|
||||
Colors.black,
|
||||
item['content'],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
// 设定字幕轨道
|
||||
setSubtitle(int index) {
|
||||
if (index == 0) {
|
||||
_videoPlayerController?.setSubtitleTrack(SubtitleTrack.no());
|
||||
_vttSubtitlesIndex.value = 0;
|
||||
vttSubtitlesIndex.value = 0;
|
||||
return;
|
||||
}
|
||||
Map<String, String> s = _vttSubtitles[index];
|
||||
Map<String, String> s = vttSubtitles[index];
|
||||
_videoPlayerController?.setSubtitleTrack(SubtitleTrack.data(
|
||||
s['text']!,
|
||||
title: s['title']!,
|
||||
language: s['language']!,
|
||||
));
|
||||
_vttSubtitlesIndex.value = index;
|
||||
vttSubtitlesIndex.value = index;
|
||||
}
|
||||
|
||||
static void updatePlayCount() {
|
||||
|
||||
@@ -10,4 +10,5 @@ enum BottomControlType {
|
||||
speed,
|
||||
fullscreen,
|
||||
custom,
|
||||
viewPoints,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPalaX/common/widgets/segment_progress_bar.dart';
|
||||
import 'package:PiliPalaX/http/loading_state.dart';
|
||||
@@ -47,6 +48,7 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
this.customWidget,
|
||||
this.customWidgets,
|
||||
this.showEpisodes,
|
||||
this.showViewPoints,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -62,6 +64,7 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
final Widget? customWidget;
|
||||
final List<Widget>? customWidgets;
|
||||
final Function? showEpisodes;
|
||||
final VoidCallback? showViewPoints;
|
||||
|
||||
@override
|
||||
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||
@@ -236,7 +239,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
Map<BottomControlType, Widget> videoProgressWidgets = {
|
||||
/// 上一集
|
||||
BottomControlType.pre: Container(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: ComBtn(
|
||||
@@ -268,7 +271,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
/// 下一集
|
||||
BottomControlType.next: Container(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: ComBtn(
|
||||
@@ -330,9 +333,32 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
/// 空白占位
|
||||
BottomControlType.space: const Spacer(),
|
||||
|
||||
/// 分段信息
|
||||
BottomControlType.viewPoints: Obx(
|
||||
() => plPlayerController.viewPointList.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: ComBtn(
|
||||
icon: Transform.rotate(
|
||||
angle: pi / 2,
|
||||
child: const Icon(
|
||||
Icons.reorder,
|
||||
semanticLabel: '分段信息',
|
||||
size: 22,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
fuc: widget.showViewPoints,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 选集
|
||||
BottomControlType.episode: Container(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: ComBtn(
|
||||
@@ -387,7 +413,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
/// 画面比例
|
||||
BottomControlType.fit: SizedBox(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
child: TextButton(
|
||||
onPressed: () => plPlayerController.toggleVideoFit(),
|
||||
@@ -408,7 +434,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
() => plPlayerController.vttSubtitles.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: SizedBox(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
child: PopupMenuButton<int>(
|
||||
onSelected: (int value) {
|
||||
@@ -434,7 +460,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}).toList();
|
||||
},
|
||||
child: Container(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: const Icon(
|
||||
@@ -450,7 +476,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
/// 播放速度
|
||||
BottomControlType.speed: SizedBox(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
child: PopupMenuButton<double>(
|
||||
onSelected: (double value) {
|
||||
@@ -473,7 +499,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}).toList();
|
||||
},
|
||||
child: Container(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: Obx(() => Text("${plPlayerController.playbackSpeed}X",
|
||||
@@ -485,7 +511,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
/// 全屏
|
||||
BottomControlType.fullscreen: SizedBox(
|
||||
width: 42,
|
||||
width: 35,
|
||||
height: 30,
|
||||
child: Obx(() => ComBtn(
|
||||
icon: Icon(
|
||||
@@ -510,6 +536,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
if (anySeason) BottomControlType.pre,
|
||||
if (anySeason) BottomControlType.next,
|
||||
BottomControlType.space,
|
||||
BottomControlType.viewPoints,
|
||||
if (anySeason) BottomControlType.episode,
|
||||
if (plPlayerController.isFullScreen.value) BottomControlType.fit,
|
||||
BottomControlType.subtitle,
|
||||
@@ -1114,7 +1141,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
segmentColors: plPlayerController.segmentList,
|
||||
),
|
||||
),
|
||||
if (plPlayerController.viewPointList.isNotEmpty)
|
||||
if (plPlayerController.viewPointList.isNotEmpty &&
|
||||
plPlayerController.showVP.value)
|
||||
CustomPaint(
|
||||
size: Size(double.infinity, 3.5),
|
||||
painter: SegmentProgressBar(
|
||||
|
||||
@@ -104,7 +104,8 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
segmentColors: controller!.segmentList,
|
||||
),
|
||||
),
|
||||
if (controller?.viewPointList.isNotEmpty == true)
|
||||
if (controller?.viewPointList.isNotEmpty == true &&
|
||||
controller?.showVP.value == true)
|
||||
CustomPaint(
|
||||
size: Size(double.infinity, 3.5),
|
||||
painter: SegmentProgressBar(
|
||||
|
||||
Reference in New Issue
Block a user