mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-30 23:29:47 +08:00
feat: sponsorblock: post segments (#9)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
27
lib/common/widgets/icon_button.dart
Normal file
27
lib/common/widgets/icon_button.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Widget iconButton({
|
||||||
|
required BuildContext context,
|
||||||
|
String? tooltip,
|
||||||
|
required IconData icon,
|
||||||
|
required VoidCallback? onPressed,
|
||||||
|
double size = 36,
|
||||||
|
}) {
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: tooltip,
|
||||||
|
onPressed: onPressed,
|
||||||
|
icon: Icon(
|
||||||
|
icon,
|
||||||
|
size: size / 2,
|
||||||
|
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
|
),
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:PiliPalaX/common/constants.dart';
|
||||||
|
import 'package:PiliPalaX/common/widgets/icon_button.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/pair.dart';
|
import 'package:PiliPalaX/common/widgets/pair.dart';
|
||||||
import 'package:PiliPalaX/common/widgets/segment_progress_bar.dart';
|
import 'package:PiliPalaX/common/widgets/segment_progress_bar.dart';
|
||||||
import 'package:PiliPalaX/http/danmaku.dart';
|
import 'package:PiliPalaX/http/danmaku.dart';
|
||||||
@@ -7,6 +10,7 @@ import 'package:PiliPalaX/http/init.dart';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:floating/floating.dart';
|
import 'package:floating/floating.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -115,6 +119,19 @@ class SegmentModel {
|
|||||||
bool hasSkipped;
|
bool hasSkipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PostSegmentModel {
|
||||||
|
PostSegmentModel({
|
||||||
|
required this.segment,
|
||||||
|
required this.category,
|
||||||
|
required this.actionType,
|
||||||
|
});
|
||||||
|
Pair<int, int> segment;
|
||||||
|
SegmentType category;
|
||||||
|
ActionType actionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ActionType { skip, mute, full, poi }
|
||||||
|
|
||||||
class VideoDetailController extends GetxController
|
class VideoDetailController extends GetxController
|
||||||
with GetSingleTickerProviderStateMixin {
|
with GetSingleTickerProviderStateMixin {
|
||||||
/// 路由传参
|
/// 路由传参
|
||||||
@@ -159,6 +176,8 @@ class VideoDetailController extends GetxController
|
|||||||
RxInt oid = 0.obs;
|
RxInt oid = 0.obs;
|
||||||
|
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
final childKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
RxString bgCover = ''.obs;
|
RxString bgCover = ''.obs;
|
||||||
PlPlayerController plPlayerController = PlPlayerController.getInstance()
|
PlPlayerController plPlayerController = PlPlayerController.getInstance()
|
||||||
..setCurrBrightness(-1.0);
|
..setCurrBrightness(-1.0);
|
||||||
@@ -183,7 +202,7 @@ class VideoDetailController extends GetxController
|
|||||||
late String cacheSecondDecode;
|
late String cacheSecondDecode;
|
||||||
late int cacheAudioQa;
|
late int cacheAudioQa;
|
||||||
|
|
||||||
late final bool _enableSponsorBlock;
|
late final bool enableSponsorBlock;
|
||||||
PlayerStatus? playerStatus;
|
PlayerStatus? playerStatus;
|
||||||
StreamSubscription<Duration>? positionSubscription;
|
StreamSubscription<Duration>? positionSubscription;
|
||||||
|
|
||||||
@@ -244,9 +263,9 @@ class VideoDetailController extends GetxController
|
|||||||
cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
||||||
defaultValue: AudioQuality.hiRes.code);
|
defaultValue: AudioQuality.hiRes.code);
|
||||||
oid.value = IdUtils.bv2av(Get.parameters['bvid']!);
|
oid.value = IdUtils.bv2av(Get.parameters['bvid']!);
|
||||||
_enableSponsorBlock =
|
enableSponsorBlock =
|
||||||
setting.get(SettingBoxKey.enableSponsorBlock, defaultValue: false);
|
setting.get(SettingBoxKey.enableSponsorBlock, defaultValue: false);
|
||||||
if (_enableSponsorBlock) {
|
if (enableSponsorBlock) {
|
||||||
_blockLimit = GStorage.blockLimit;
|
_blockLimit = GStorage.blockLimit;
|
||||||
_blockSettings = GStorage.blockSettings;
|
_blockSettings = GStorage.blockSettings;
|
||||||
_blockColor = GStorage.blockColor;
|
_blockColor = GStorage.blockColor;
|
||||||
@@ -263,14 +282,17 @@ class VideoDetailController extends GetxController
|
|||||||
_blockColor?[segment.index] ?? segment.color;
|
_blockColor?[segment.index] ?? segment.color;
|
||||||
|
|
||||||
Future _vote(String uuid, int type) async {
|
Future _vote(String uuid, int type) async {
|
||||||
Request().post(
|
Request()
|
||||||
|
.post(
|
||||||
'${GStorage.blockServer}/api/voteOnSponsorTime',
|
'${GStorage.blockServer}/api/voteOnSponsorTime',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'UUID': uuid,
|
'UUID': uuid,
|
||||||
'userID': GStorage.blockUserID,
|
'userID': GStorage.blockUserID,
|
||||||
'type': type,
|
'type': type,
|
||||||
},
|
},
|
||||||
).then((res) {
|
options: _options,
|
||||||
|
)
|
||||||
|
.then((res) {
|
||||||
SmartDialog.showToast(res.statusCode == 200 ? '投票成功' : '投票失败');
|
SmartDialog.showToast(res.statusCode == 200 ? '投票成功' : '投票失败');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -289,14 +311,17 @@ class VideoDetailController extends GetxController
|
|||||||
dense: true,
|
dense: true,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.back();
|
Get.back();
|
||||||
Request().post(
|
Request()
|
||||||
|
.post(
|
||||||
'${GStorage.blockServer}/api/voteOnSponsorTime',
|
'${GStorage.blockServer}/api/voteOnSponsorTime',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'UUID': segment.UUID,
|
'UUID': segment.UUID,
|
||||||
'userID': GStorage.blockUserID,
|
'userID': GStorage.blockUserID,
|
||||||
'category': item.name,
|
'category': item.name,
|
||||||
},
|
},
|
||||||
).then((res) {
|
options: _options,
|
||||||
|
)
|
||||||
|
.then((res) {
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(
|
||||||
'类别更改${res.statusCode == 200 ? '成功' : '失败'}');
|
'类别更改${res.statusCode == 200 ? '成功' : '失败'}');
|
||||||
});
|
});
|
||||||
@@ -387,7 +412,7 @@ class VideoDetailController extends GetxController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showSponsorBlock(BuildContext context) {
|
void showSBDetail(BuildContext context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
@@ -502,14 +527,7 @@ class VideoDetailController extends GetxController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _sponsorBlock() async {
|
Options get _options => Options(
|
||||||
dynamic result = await Request().get(
|
|
||||||
'${GStorage.blockServer}/api/skipSegments',
|
|
||||||
data: {
|
|
||||||
'videoID': bvid,
|
|
||||||
'cid': cid.value,
|
|
||||||
},
|
|
||||||
options: Options(
|
|
||||||
headers: {
|
headers: {
|
||||||
'env': '',
|
'env': '',
|
||||||
'app-key': '',
|
'app-key': '',
|
||||||
@@ -519,7 +537,16 @@ class VideoDetailController extends GetxController
|
|||||||
HttpHeaders.cookieHeader:
|
HttpHeaders.cookieHeader:
|
||||||
'buvid3= ; SESSDATA= ; bili_jct= ; DedeUserID= ; DedeUserID__ckMd5= ; sid= ',
|
'buvid3= ; SESSDATA= ; bili_jct= ; DedeUserID= ; DedeUserID__ckMd5= ; sid= ',
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
|
|
||||||
|
Future _sponsorBlock() async {
|
||||||
|
dynamic result = await Request().get(
|
||||||
|
'${GStorage.blockServer}/api/skipSegments',
|
||||||
|
data: {
|
||||||
|
'videoID': bvid,
|
||||||
|
'cid': cid.value,
|
||||||
|
},
|
||||||
|
options: _options,
|
||||||
);
|
);
|
||||||
if (result.data is List && result.data.isNotEmpty) {
|
if (result.data is List && result.data.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
@@ -608,6 +635,7 @@ class VideoDetailController extends GetxController
|
|||||||
Request().post(
|
Request().post(
|
||||||
'${GStorage.blockServer}/api/viewedVideoSponsorTime',
|
'${GStorage.blockServer}/api/viewedVideoSponsorTime',
|
||||||
queryParameters: {'UUID': item.UUID},
|
queryParameters: {'UUID': item.UUID},
|
||||||
|
options: _options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -832,7 +860,7 @@ class VideoDetailController extends GetxController
|
|||||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
data = result['data'];
|
data = result['data'];
|
||||||
if (_enableSponsorBlock) {
|
if (enableSponsorBlock) {
|
||||||
await _sponsorBlock();
|
await _sponsorBlock();
|
||||||
}
|
}
|
||||||
if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) {
|
if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) {
|
||||||
@@ -974,4 +1002,371 @@ class VideoDetailController extends GetxController
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<PostSegmentModel>? list;
|
||||||
|
|
||||||
|
void onBlock(BuildContext context) {
|
||||||
|
PersistentBottomSheetController? ctr;
|
||||||
|
list ??= <PostSegmentModel>[];
|
||||||
|
if (list!.isEmpty) {
|
||||||
|
list!.add(
|
||||||
|
PostSegmentModel(
|
||||||
|
segment: Pair(first: 0, second: 0),
|
||||||
|
category: SegmentType.sponsor,
|
||||||
|
actionType: ActionType.skip,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ctr = plPlayerController.isFullScreen.value
|
||||||
|
? scaffoldKey.currentState?.showBottomSheet(
|
||||||
|
enableDrag: false,
|
||||||
|
(context) => _postPanel(ctr?.close),
|
||||||
|
)
|
||||||
|
: childKey.currentState?.showBottomSheet(
|
||||||
|
enableDrag: false,
|
||||||
|
(context) => _postPanel(ctr?.close),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _postPanel(onClose) => StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
List<Widget> segmentWidget({
|
||||||
|
required int index,
|
||||||
|
required bool isFirst,
|
||||||
|
}) {
|
||||||
|
String value = Utils.timeFormat(isFirst
|
||||||
|
? list![index].segment.first
|
||||||
|
: list![index].segment.second);
|
||||||
|
return [
|
||||||
|
Text(
|
||||||
|
'${isFirst ? '开始' : '结束'}: $value',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
iconButton(
|
||||||
|
context: context,
|
||||||
|
size: 26,
|
||||||
|
tooltip: '使用当前位置',
|
||||||
|
icon: Icons.my_location,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
if (isFirst) {
|
||||||
|
list![index].segment.first =
|
||||||
|
plPlayerController.positionSeconds.value;
|
||||||
|
} else {
|
||||||
|
list![index].segment.second =
|
||||||
|
plPlayerController.positionSeconds.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
iconButton(
|
||||||
|
context: context,
|
||||||
|
size: 26,
|
||||||
|
tooltip: '编辑',
|
||||||
|
icon: Icons.edit,
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) {
|
||||||
|
String initV = value;
|
||||||
|
return AlertDialog(
|
||||||
|
content: TextFormField(
|
||||||
|
initialValue: value,
|
||||||
|
autofocus: true,
|
||||||
|
onChanged: (value) {
|
||||||
|
initV = value;
|
||||||
|
},
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'[\d:]+'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: Get.back,
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(result: initV),
|
||||||
|
child: Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).then((res) {
|
||||||
|
if (res != null) {
|
||||||
|
try {
|
||||||
|
List<int> split = (res as String)
|
||||||
|
.split(':')
|
||||||
|
.toList()
|
||||||
|
.reversed
|
||||||
|
.toList()
|
||||||
|
.map((e) => int.parse(e))
|
||||||
|
.toList();
|
||||||
|
int duration = 0;
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
duration += split[i] * pow(60, i).toInt();
|
||||||
|
}
|
||||||
|
if (duration <= (data.timeLength ?? 0) / 1000) {
|
||||||
|
setState(() {
|
||||||
|
if (isFirst) {
|
||||||
|
list![index].segment.first = duration;
|
||||||
|
} else {
|
||||||
|
list![index].segment.second = duration;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
titleSpacing: 16,
|
||||||
|
title: const Text('提交片段'),
|
||||||
|
actions: [
|
||||||
|
iconButton(
|
||||||
|
context: context,
|
||||||
|
tooltip: '添加片段',
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
list?.insert(
|
||||||
|
0,
|
||||||
|
PostSegmentModel(
|
||||||
|
segment: Pair(first: 0, second: 0),
|
||||||
|
category: SegmentType.sponsor,
|
||||||
|
actionType: ActionType.skip,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icons.add,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
iconButton(
|
||||||
|
context: context,
|
||||||
|
tooltip: '关闭',
|
||||||
|
onPressed: onClose,
|
||||||
|
icon: Icons.close,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: list?.isNotEmpty == true
|
||||||
|
? Stack(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...List.generate(
|
||||||
|
list!.length,
|
||||||
|
(index) => Container(
|
||||||
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 5,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onInverseSurface,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
...segmentWidget(
|
||||||
|
isFirst: true,
|
||||||
|
index: index,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
...segmentWidget(
|
||||||
|
isFirst: false,
|
||||||
|
index: index,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
iconButton(
|
||||||
|
context: context,
|
||||||
|
size: 26,
|
||||||
|
icon: Icons.clear,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
list!.removeAt(index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text('分类: '),
|
||||||
|
PopupMenuButton(
|
||||||
|
initialValue: list![index].category,
|
||||||
|
onSelected: (item) async {
|
||||||
|
setState(() {
|
||||||
|
list![index].category = item;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => SegmentType
|
||||||
|
.values
|
||||||
|
.map((item) =>
|
||||||
|
PopupMenuItem<SegmentType>(
|
||||||
|
value: item,
|
||||||
|
child: Text(item.title),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
list![index].category.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
const Text('ActionType: '),
|
||||||
|
PopupMenuButton(
|
||||||
|
initialValue: list![index].actionType,
|
||||||
|
onSelected: (item) async {
|
||||||
|
setState(() {
|
||||||
|
list![index].actionType = item;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => ActionType
|
||||||
|
.values
|
||||||
|
.map((item) =>
|
||||||
|
PopupMenuItem<ActionType>(
|
||||||
|
value: item,
|
||||||
|
child: Text(item.name),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
list![index].actionType.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
size: 20,
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 88 + MediaQuery.paddingOf(context).bottom,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 16,
|
||||||
|
bottom: 16 + MediaQuery.paddingOf(context).bottom,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
tooltip: '提交',
|
||||||
|
onPressed: () {
|
||||||
|
Request()
|
||||||
|
.post(
|
||||||
|
'${GStorage.blockServer}/api/skipSegments',
|
||||||
|
queryParameters: {
|
||||||
|
'videoID': bvid,
|
||||||
|
'cid': cid.value,
|
||||||
|
'userID': GStorage.blockUserID,
|
||||||
|
'userAgent': Constants.userAgent,
|
||||||
|
'videoDuration': (data.timeLength ?? 0 / 1000),
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
'segments': list!
|
||||||
|
.map(
|
||||||
|
(item) => {
|
||||||
|
'segment': [
|
||||||
|
item.segment.first,
|
||||||
|
item.segment.second,
|
||||||
|
],
|
||||||
|
'category': item.category.name,
|
||||||
|
'actionType': item.actionType.name,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
},
|
||||||
|
options: _options,
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(res) {
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
Get.back();
|
||||||
|
SmartDialog.showToast('提交成功');
|
||||||
|
list?.clear();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(
|
||||||
|
'提交失败: ${{
|
||||||
|
400: '参数错误',
|
||||||
|
403: '被自动审核机制拒绝',
|
||||||
|
429: '重复提交太快',
|
||||||
|
409: '重复提交'
|
||||||
|
}[res.statusCode]}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Icon(Icons.check),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
// StreamSubscription<Duration>? _bufferedListener;
|
// StreamSubscription<Duration>? _bufferedListener;
|
||||||
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
|
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
|
||||||
|
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -520,7 +518,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: scaffoldKey,
|
key: videoDetailController.childKey,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -578,7 +576,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: scaffoldKey,
|
key: videoDetailController.childKey,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -630,7 +628,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: scaffoldKey,
|
key: videoDetailController.childKey,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -685,7 +683,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Expanded(
|
child: Expanded(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: scaffoldKey,
|
key: videoDetailController.childKey,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -785,7 +783,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
height: context.height -
|
height: context.height -
|
||||||
(removeSafeArea ? 0 : MediaQuery.of(context).padding.top),
|
(removeSafeArea ? 0 : MediaQuery.of(context).padding.top),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: scaffoldKey,
|
key: videoDetailController.childKey,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -1289,7 +1287,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
|
|
||||||
// 展示二级回复
|
// 展示二级回复
|
||||||
void replyReply(replyItem, id) {
|
void replyReply(replyItem, id) {
|
||||||
scaffoldKey.currentState?.showBottomSheet(
|
videoDetailController.childKey.currentState?.showBottomSheet(
|
||||||
(context) => VideoReplyReplyPanel(
|
(context) => VideoReplyReplyPanel(
|
||||||
id: id,
|
id: id,
|
||||||
// rcount: replyItem.rcount,
|
// rcount: replyItem.rcount,
|
||||||
@@ -1304,14 +1302,14 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
|
|
||||||
// ai总结
|
// ai总结
|
||||||
showAiBottomSheet() {
|
showAiBottomSheet() {
|
||||||
scaffoldKey.currentState?.showBottomSheet(
|
videoDetailController.childKey.currentState?.showBottomSheet(
|
||||||
enableDrag: true,
|
enableDrag: true,
|
||||||
(context) => AiDetail(modelResult: videoIntroController.modelResult),
|
(context) => AiDetail(modelResult: videoIntroController.modelResult),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
showIntroDetail(videoDetail, videoTags) {
|
showIntroDetail(videoDetail, videoTags) {
|
||||||
scaffoldKey.currentState?.showBottomSheet(
|
videoDetailController.childKey.currentState?.showBottomSheet(
|
||||||
enableDrag: true,
|
enableDrag: true,
|
||||||
(context) => videoDetail is BangumiInfoModel
|
(context) => videoDetail is BangumiInfoModel
|
||||||
? bangumi.IntroDetail(
|
? bangumi.IntroDetail(
|
||||||
@@ -1339,7 +1337,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
context: context,
|
context: context,
|
||||||
scaffoldState: isFullScreen
|
scaffoldState: isFullScreen
|
||||||
? videoDetailController.scaffoldKey.currentState
|
? videoDetailController.scaffoldKey.currentState
|
||||||
: scaffoldKey.currentState,
|
: videoDetailController.childKey.currentState,
|
||||||
).buildShowBottomSheet();
|
).buildShowBottomSheet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
RxString now = ''.obs;
|
RxString now = ''.obs;
|
||||||
Timer? clock;
|
Timer? clock;
|
||||||
late String defaultCDNService;
|
late String defaultCDNService;
|
||||||
|
|
||||||
bool get isFullScreen => widget.controller!.isFullScreen.value;
|
bool get isFullScreen => widget.controller!.isFullScreen.value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1450,20 +1449,37 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// ),
|
// ),
|
||||||
// fuc: () => _.screenshot(),
|
// fuc: () => _.screenshot(),
|
||||||
// ),
|
// ),
|
||||||
|
if (widget.videoDetailCtr?.enableSponsorBlock == true)
|
||||||
|
SizedBox(
|
||||||
|
width: 42,
|
||||||
|
height: 34,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: '提交片段',
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () => widget.videoDetailCtr?.onBlock(context),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.block,
|
||||||
|
size: 19,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => widget.videoDetailCtr?.segmentList.isNotEmpty == true
|
() => widget.videoDetailCtr?.segmentList.isNotEmpty == true
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: 42,
|
width: 42,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: 'SponsorBlock',
|
tooltip: '片段信息',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
widget.videoDetailCtr?.showSponsorBlock(context),
|
widget.videoDetailCtr?.showSBDetail(context),
|
||||||
icon: const Icon(
|
icon: Icon(
|
||||||
Icons.block,
|
MdiIcons.advertisements,
|
||||||
size: 19,
|
size: 19,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -255,6 +255,12 @@ class SettingBoxKey {
|
|||||||
enableAi = 'enableAi',
|
enableAi = 'enableAi',
|
||||||
disableLikeMsg = 'disableLikeMsg',
|
disableLikeMsg = 'disableLikeMsg',
|
||||||
defaultHomePage = 'defaultHomePage',
|
defaultHomePage = 'defaultHomePage',
|
||||||
|
previewQuality = 'previewQuality',
|
||||||
|
checkDynamic = 'checkDynamic',
|
||||||
|
dynamicPeriod = 'dynamicPeriod',
|
||||||
|
schemeVariant = 'schemeVariant',
|
||||||
|
|
||||||
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
blockSettings = 'blockSettings',
|
blockSettings = 'blockSettings',
|
||||||
blockLimit = 'blockLimit',
|
blockLimit = 'blockLimit',
|
||||||
@@ -263,10 +269,6 @@ class SettingBoxKey {
|
|||||||
blockToast = 'blockToast',
|
blockToast = 'blockToast',
|
||||||
blockServer = 'blockServer',
|
blockServer = 'blockServer',
|
||||||
blockTrack = 'blockTrack',
|
blockTrack = 'blockTrack',
|
||||||
previewQuality = 'previewQuality',
|
|
||||||
checkDynamic = 'checkDynamic',
|
|
||||||
dynamicPeriod = 'dynamicPeriod',
|
|
||||||
schemeVariant = 'schemeVariant',
|
|
||||||
|
|
||||||
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细
|
// 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细
|
||||||
danmakuWeight = 'danmakuWeight',
|
danmakuWeight = 'danmakuWeight',
|
||||||
|
|||||||
Reference in New Issue
Block a user