mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-30 23:58:13 +08:00
refa: sb & feat: sb portVideo (WIP) (#1751)
* refa: sb * feat: sb portVideo (WIP) * fix: keep-alive * revert: ua version * fix * tweak [skip ci] Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> --------- Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
d5d95671ff
commit
2be13e7283
@@ -103,6 +103,8 @@ class Request {
|
||||
//Http请求头.
|
||||
headers: {
|
||||
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
|
||||
if (!Pref.enableHttp2) 'connection': 'keep-alive',
|
||||
'accept-encoding': 'br,gzip',
|
||||
},
|
||||
responseDecoder: _responseDecoder, // Http2Adapter没有自动解压
|
||||
persistentConnection: true,
|
||||
|
||||
222
lib/http/sponsor_block.dart
Normal file
222
lib/http/sponsor_block.dart
Normal file
@@ -0,0 +1,222 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/sponsor_block_api.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/post_segment_model.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
|
||||
import 'package:PiliPlus/models_new/sponsor_block/segment_item.dart';
|
||||
import 'package:PiliPlus/models_new/sponsor_block/user_info.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
/// https://github.com/hanydd/BilibiliSponsorBlock/wiki/API
|
||||
abstract final class SponsorBlock {
|
||||
static String get blockServer => Pref.blockServer;
|
||||
static final options = Options(
|
||||
followRedirects: true,
|
||||
validateStatus: (status) => true,
|
||||
);
|
||||
|
||||
static Error getErrMsg(Response res) {
|
||||
String statusMessage = switch (res.statusCode) {
|
||||
200 => '意料之外的响应',
|
||||
400 => '参数错误',
|
||||
403 => '被自动审核机制拒绝',
|
||||
404 => '未找到数据',
|
||||
409 => '重复提交',
|
||||
429 => '提交太快(触发速率控制)',
|
||||
500 => '服务器无法获取信息',
|
||||
-1 => res.data['message'].toString(), // DioException
|
||||
_ => res.statusMessage ?? res.statusCode.toString(),
|
||||
};
|
||||
if (res.statusCode != null && res.statusCode != -1) {
|
||||
final data = res.data;
|
||||
if (res.statusCode == 200 ||
|
||||
(data is String && data.isNotEmpty && data.length < 200)) {
|
||||
statusMessage = '$statusMessage:$data';
|
||||
}
|
||||
}
|
||||
return Error(statusMessage, code: res.statusCode);
|
||||
}
|
||||
|
||||
static String _api(String url) => '$blockServer/api/$url';
|
||||
|
||||
static Future<LoadingState<List<SegmentItemModel>>> getSkipSegments({
|
||||
required String bvid,
|
||||
required int cid,
|
||||
}) async {
|
||||
final res = await Request().get(
|
||||
_api(SponsorBlockApi.skipSegments),
|
||||
queryParameters: {
|
||||
'videoID': bvid,
|
||||
'cid': cid,
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
if (res.data case List list) {
|
||||
return Success(list.map((i) => SegmentItemModel.fromJson(i)).toList());
|
||||
}
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<Null>> voteOnSponsorTime({
|
||||
required String uuid,
|
||||
int? type,
|
||||
SegmentType? category,
|
||||
}) async {
|
||||
assert((type == null) == (category == null));
|
||||
final res = await Request().post(
|
||||
_api(SponsorBlockApi.voteOnSponsorTime),
|
||||
data: {
|
||||
'UUID': uuid,
|
||||
'type': ?type,
|
||||
'category': ?category?.name,
|
||||
'userID': Pref.blockUserID,
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
return res.statusCode == 200 ? const Success(null) : getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<Null>> viewedVideoSponsorTime(String uuid) async {
|
||||
final res = await Request().post(
|
||||
_api(SponsorBlockApi.viewedVideoSponsorTime),
|
||||
data: {'UUID': uuid},
|
||||
options: options,
|
||||
);
|
||||
return res.statusCode == 200 ? const Success(null) : getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<Null>> uptimeStatus() async {
|
||||
final res = await Request().get(
|
||||
_api(SponsorBlockApi.uptimeStatus),
|
||||
options: options,
|
||||
);
|
||||
if (res.statusCode == 200 &&
|
||||
res.data is String &&
|
||||
Utils.isStringNumeric(res.data)) {
|
||||
return const Success(null);
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<UserInfo>> userInfo(
|
||||
List<String> query, {
|
||||
String? userId,
|
||||
}) async {
|
||||
final res = await Request().get(
|
||||
_api(SponsorBlockApi.userInfo),
|
||||
queryParameters: {
|
||||
'userID': userId ?? Pref.blockUserID,
|
||||
'values': jsonEncode(query),
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
return Success(UserInfo.fromJson(res.data));
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<List<SegmentItemModel>>> postSkipSegments({
|
||||
required String bvid,
|
||||
required int cid,
|
||||
required double videoDuration,
|
||||
required List<PostSegmentModel> segments,
|
||||
}) async {
|
||||
final res = await Request().post(
|
||||
_api(SponsorBlockApi.skipSegments),
|
||||
data: {
|
||||
'videoID': bvid,
|
||||
'cid': cid.toString(),
|
||||
'userID': Pref.blockUserID,
|
||||
'userAgent': '${Constants.appName}/${BuildConfig.versionName}',
|
||||
'videoDuration': videoDuration,
|
||||
'segments': segments
|
||||
.map(
|
||||
(item) => {
|
||||
'segment': [item.segment.first, item.segment.second],
|
||||
'category': item.category.name,
|
||||
'actionType': item.actionType.name,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
if (res.data case List list) {
|
||||
return Success(list.map((i) => SegmentItemModel.fromJson(i)).toList());
|
||||
}
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
|
||||
/// {
|
||||
/// "bvID": string, // B站视频BVID
|
||||
/// "cid": string, // 视频CID
|
||||
/// "ytbID": string, // YouTube视频ID
|
||||
/// "UUID": string, // 绑定记录的UUID(不是视频中片段的UUID,是绑定记录本身的UUID)
|
||||
/// "votes": int, // 绑定记录的投票数
|
||||
/// "locked": int, // 绑定记录是否锁定
|
||||
/// }
|
||||
/// TODO: show port video info dialog
|
||||
static Future<LoadingState<String>> getPortVideo({
|
||||
required String bvid,
|
||||
required int cid,
|
||||
}) async {
|
||||
final res = await Request().get(
|
||||
_api(SponsorBlockApi.portVideo),
|
||||
queryParameters: {
|
||||
'videoID': bvid,
|
||||
'cid': cid.toString(),
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
if (res.data case Map<String, dynamic> data) {
|
||||
if (data['ytbID'] case String ytbId) {
|
||||
return Success(ytbId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
|
||||
static Future<LoadingState<String>> postPortVideo({
|
||||
required String bvid,
|
||||
required int cid,
|
||||
required String ytbId,
|
||||
required int videoDuration,
|
||||
}) async {
|
||||
final res = await Request().post(
|
||||
_api(SponsorBlockApi.portVideo),
|
||||
data: {
|
||||
'bvID': bvid,
|
||||
'cid': cid.toString(),
|
||||
'ytbID': ytbId,
|
||||
'userID': Pref.blockUserID,
|
||||
'biliDuration': videoDuration,
|
||||
},
|
||||
options: options,
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
if (res.data case Map<String, dynamic> data) {
|
||||
if (data['UUID'] case String uuid) {
|
||||
return Success(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getErrMsg(res);
|
||||
}
|
||||
}
|
||||
9
lib/http/sponsor_block_api.dart
Normal file
9
lib/http/sponsor_block_api.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
abstract final class SponsorBlockApi {
|
||||
static const String skipSegments = 'skipSegments';
|
||||
static const String voteOnSponsorTime = 'voteOnSponsorTime';
|
||||
static const String viewedVideoSponsorTime = 'viewedVideoSponsorTime';
|
||||
static const String portVideo = 'portVideo';
|
||||
|
||||
static const String userInfo = 'userInfo';
|
||||
static const String uptimeStatus = 'status/uptime';
|
||||
}
|
||||
Reference in New Issue
Block a user